IBOutlet因EXC_BAD_ACCESS而崩溃,即使不是nil

时间:2018-12-07 02:30:20

标签: ios swift uiviewcontroller exc-bad-access swift-protocols

在UIViewController(rolePageController)中,我配置了另一个UIViewController(drawerController),并从角色页面向其传递2个UIViews,这将是抽屉控制器配置的一部分。一旦抽屉控制器尝试从rolePageController访问IBOutlet视图,它就会崩溃并显示EXC_BAD_ACCESS(code = EXC_I386_GPFLT)。

在第一个VC(rolePageController)中,这是IBOutlets:

@IBOutlet var rolePageDrawerView: UIView!
@IBOutlet var rolePageContentView: UIView!

在rolePageController.viewDidLoad()中,我调用了drawerController.configureDrawer(...):

override func viewDidLoad() {
    super.viewDidLoad()

    //other stuff happens here

    let drawerController = UIStoryboard(name: "StoryboardName", bundle: nil).instantiateViewController(withIdentifier: "drawerController") as! DrawerViewController
    drawerController.configureDrawer(drawerContainerView: self.rolePageDrawerView, overlaidView: self.rolePageContentView)

    //other stuff here
}

DrawerViewController协议定义为:

protocol DrawerViewController where Self: UIViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}

这是configureDrawer(...)函数的代码:

private var drawerParentView: UIView!
private var overlaidByDrawerView: UIView!


func configureDrawer(drawerContainerView: UIView, overlaidView: UIView) {
    self.drawerParentView = drawerContainerView
    self.overlaidByDrawerView = overlaidView
}

在调试器中指出,所调用的抽屉控制器实例与接收该调用的自身实例不匹配。这是将被调用的实例的地址:

enter image description here

这是我步入通话时实例的地址:

enter image description here

当我进入该调用时,调用之前抽屉控制器的地址不是自己的地址。那永远不会发生。

我创建了一个简化的项目,可以复制https://github.com/ksoftllc/DynamicStackBufferOverflow处的崩溃。

解决方案 原来的解决方案是从DrawerViewController协议中删除where子句。

protocol DrawerViewController where Self: UIViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}

5 个答案:

答案 0 :(得分:8)

找到了令人反感的代码,但是我不知道为什么这会导致我看到的错误。抽屉控制器符合DrawerViewController协议,定义为:

int magnitude = (int)Math.Pow(10, (int)Math.Log10(value));

当我删除Where条件时,它不再崩溃。

protocol DrawerViewController where Self: UIViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}

where子句对于程序的正确功能实际上并不是必需的,因此我将在没有它的情况下继续进行。

更新 我向swift.org提交了错误,并收到了回复。 Swift 4.2不支持在协议中添加where子句,但是Swift 5.0将支持添加子句。此外,@ J Doe在下面发布了一种通过更新Xcode工具包来实现此目的的方法。

答案 1 :(得分:5)

dynamic-stack-buffer-overflow与递归无关。这意味着alloca缓冲区已溢出。检查asan runtime source code

假设堆栈布局合理,这样您就有一个alloca缓冲区和一个对象指针,甚至可能是作为参数传递的对象指针之一。

假设alloca缓冲区溢出。在asan构建中,这可能会触发dynamic-stack-buffer-overflow错误。但是在非asan的构建中,它仅覆盖该对象指针的字节。假设它写入的字节构成了未在进程的页表中映射的地址。

如果程序尝试读取该对象指针并将其存储在其他位置(例如,在实例变量中),则它必须增加该对象的引用计数。但这意味着取消对指针的引用,并且指针指向未映射的地址。也许这会导致一般性保护错误,Mach calls an EXC_I386_GPFLT

如果您发布了asan dynamic-stack-buffer-overflow错误的堆栈跟踪信息,并且对导致错误的代码进行了反汇编,将会很有帮助。

答案 2 :(得分:3)

它看起来确实像Swift编译器错误。为简化起见,我简化了代码:

func foo(_ wow: TestProtocol) {
    wow.foo()
}

protocol TestProtocol where Self: NSObject {
    func foo()
}

class TestClass: NSObject, TestProtocol {

    func foo() {
        print("Much wow")
    }

}

foo(TestClass())

您可以将其报告为错误。为解决此问题,我建议您不要使用where语句或类型为func foo(_ wow: TestClass {的pass对象。

答案 3 :(得分:0)

要解决您的问题,请在开发主干工具链快照上运行它。您可以在这里下载:

https://swift.org/download/

转到快照-> Trunk开发(主) XCode (不是Swift 5.0)并下载截至12月15日的快照(我从11月30日获得了快照,但是我确定12月15日也可以。)

安装工具链后,在XCode中转到:File -> Preferences -> Components并选择最新的工具链。 它现在可以正常运行了

此外,Self: UIViewController的位置可以缩短为:UIViewcontroller这仅适用于最新的工具链):

protocol DrawerViewController: UIViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}

答案 4 :(得分:-2)

将此函数调用从viewDidLoad移动到viewWillAppear nonmatch <- df[grepl("[^A-Za-z0-9[:punct:][:space:]]", df$text), ]