即使不是nil,IBOutlet也会与EXC_BAD_ACCESS崩溃

Chu*_*ger 17 exc-bad-access uiviewcontroller ios swift swift-protocols

在UIViewController(rolePageController)中,我配置另一个UIViewController(drawerController)并从角色页面传递2个UIViews,该页面将成为drawerController配置的一部分.一旦drawerController尝试从rolePageController访问IBOutlet视图,它就会崩溃EXC_BAD_ACCESS(代码= EXC_I386_GPFLT).

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

@IBOutlet var rolePageDrawerView: UIView!
@IBOutlet var rolePageContentView: UIView!
Run Code Online (Sandbox Code Playgroud)

在rolePageController.viewDidLoad()中,我调用了抽屉控件.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
}
Run Code Online (Sandbox Code Playgroud)

DrawerViewController协议定义为:

protocol DrawerViewController where Self: UIViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}
Run Code Online (Sandbox Code Playgroud)

这是configureDrawer(...)func的代码:

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


func configureDrawer(drawerContainerView: UIView, overlaidView: UIView) {
    self.drawerParentView = drawerContainerView
    self.overlaidByDrawerView = overlaidView
}
Run Code Online (Sandbox Code Playgroud)

在调试器中注意到,调用的drawerController实例与接收调用的self实例不匹配.这是将被调用的实例的地址:

在此输入图像描述

这是我进入呼叫时实例的地址:

在此输入图像描述

当我进入呼叫时,呼叫前抽屉控制器的地址不是自己的地址.这绝不应该发生.

我创建了一个简化的项目,通过https://github.com/ksoftllc/DynamicStackBufferOverflow重现崩溃.

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

protocol DrawerViewController where Self: UIViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}
Run Code Online (Sandbox Code Playgroud)

Chu*_*ger 8

找到了有问题的代码,但我不知道为什么这会导致我看到的错误.drawerController符合DrawerViewController协议,定义如下:

protocol DrawerViewController where Self: UIViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}
Run Code Online (Sandbox Code Playgroud)

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

protocol DrawerViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 还在stackoverflow上找到了这个答案,表明将where子句与协议一起使用存在已知问题,如果意图是要求某个超类作为协议的一部分,甚至建议一种解决方法./sf/answers/3545343371/ (2认同)
  • 只是fyi,我尝试了你的示例项目,它与你在原始问题中描述的略有不同.它没有在`configureDrawer`方法中崩溃*但在它之前.似乎与"DrawerView"扩展实际上没有实现所需的方法(已经直接在`DrawerViewController中完成)这一事实有关.将方法实现移动到协议的实际扩展中导致它在第二行崩溃,类似于您的方案(为什么不在1号我不知道).删除`where ...`然后再次工作. (2认同)
  • 更奇怪/提示:直接在`vc`变量上调用`configureDrawer`,而不需要临时转换为`DrawerView`也可以避免崩溃(无论方法是在扩展中实现还是直接在类中实现).这似乎表明它与一些内部铸造有关.我最终也会对错误报告本身发表评论 (2认同)

rob*_*off 5

dynamic-stack-buffer-overflow与递归没有任何关系.这意味着alloca缓冲区溢出.检查asan运行时源代码.

假设堆栈已布局,以便您有一个alloca缓冲区,后跟一个对象指针 - 甚至可能是作为参数传递的一个对象指针.

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

如果程序试图读取该对象指针并将其存储在别处(例如,在实例变量中),则必须增加对象的引用计数.但这意味着取消引用指针 - 指针指向未映射的地址.也许这会导致一般的保护错误,马赫称之为EXC_I386_GPFLT.

如果您发布了asan dynamic-stack-buffer-overflow错误的堆栈跟踪以及导致错误的代码的反汇编,那将会很有帮助.