Search code examples
iosswiftuiviewcontrollerexc-bad-accessswift-protocols

IBOutlet crashing with EXC_BAD_ACCESS even though not nil


In a UIViewController (rolePageController) I configure another UIViewController (drawerController) and pass it 2 UIViews from the role page that will be part of the drawerController's configuration. As soon as the drawerController tries to access the IBOutlet views from the rolePageController, it crashes with EXC_BAD_ACCESS (code=EXC_I386_GPFLT).

In the 1st VC (rolePageController), here are the IBOutlets:

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

In rolePageController.viewDidLoad() I make a call to the 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
}

The DrawerViewController protocol is defined as:

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

Here is the code for the configureDrawer(...) func:

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


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

Noticed in the debugger that the drawerController instance that is called does not match the self instance that receives the call. Here is the address of the instance that will be called:

enter image description here

Here is the address of the instance when I step into the call:

enter image description here

The address of drawerController before the call is not the address of self when I step into the call. That should never happen.

I have created a simplified project that reproduces the crash at https://github.com/ksoftllc/DynamicStackBufferOverflow.

Solution Solution turned out to be to remove the where clause from the DrawerViewController protocol.

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

Solution

  • Found the offending code, but I don't know why this would cause the errors I was seeing. The drawerController conforms to DrawerViewController protocol, defined as:

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

    When I remove the Where condition, it no longer crashes.

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

    The where clause was not actually necessary for correct function of the program, so I will proceed without it.

    UPDATE I filed a bug with swift.org and received a response. Adding a where clause to a protocol is not supported in Swift 4.2, but will be supported in Swift 5.0. In addition, @J Doe posted below a way to accomplish this with an update to Xcode toolkit.