Uploaded image for project: 'Swift'
  1. Swift
  2. SR-704

EXC_BAD_ACCESS on returning nil from a failable initializer of NSObject subclass

    Details

    • Type: Bug
    • Status: Resolved
    • Priority: Medium
    • Resolution: Done
    • Component/s: None
    • Labels:
    • Environment:

      Description

      Returning nil from a failable initializer of an NSObject subclass results in a runtime crash in swift_deallocPartialClassInstance call. This doesn't happen if a class method is used instead of a convenience initializer, or if the class doesn't inherit from NSObject. The issue does not depend on optimization level. This is a regression from Swift 2.1 / Xcode 7.2 where the same setup doesn't crash.

      Code example

      import Foundation
      
      class MyClass: NSObject {
      
          let property: String
      
          required init(value: String) {
              self.property = value
      
              super.init()
          }
      
          convenience init?(failableValue: String) {
              if failableValue != "" {
                  self.init(value: failableValue)
              } else {
                  return nil
              }
          }
      
          static func instanceWithFailableValue(failableValue: String) -> Self? {
              if failableValue != "" {
                  return self.init(value: failableValue)
              } else {
                  return nil
              }
          }
      
      }
      
      let goodValue = "Don't crash"
      let badValue = ""
      
      // This works correctly
      let goodObject = MyClass(failableValue: goodValue)
      print(goodObject)
      
      // This also works correctly
      let workaround = MyClass.instanceWithFailableValue(badValue)
      print(workaround)
      
      // This crashes upon returning from the initializer
      let badObject = MyClass(failableValue: badValue)
      print(badObject)
      

      Stacktrace

      * thread #1: tid = 0x1792d1, 0x0000000100259f40 DemoProject`swift::Metadata::getClassObject() const, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
        * frame #0: 0x0000000100259f40 DemoProject`swift::Metadata::getClassObject() const
          frame #1: 0x000000010025353e DemoProject`swift_deallocPartialClassInstance + 62
          frame #2: 0x000000010026b9e5 DemoProject`MyClass.init(failableValue="") -> MyClass? + 357 at main.swift:0
          frame #3: 0x000000010026ba42 DemoProject`MyClass.__allocating_init(failableValue : String) -> MyClass? + 66 at main.swift:0
          frame #4: 0x000000010026c70b DemoProject`testFailableInitializer() -> () + 603 at main.swift:42
          frame #5: 0x000000010026b67b DemoProject`main + 75 at main.swift:46
          frame #6: 0x00007fff8edcb5ad libdyld.dylib`start + 1
      

        Attachments

          Issue Links

            Activity

            Hide
            DougGregor Doug Gregor added a comment -

            I've created a PR against Swift 2.2 for this:

            https://github.com/apple/swift/pull/1798

            Show
            DougGregor Doug Gregor added a comment - I've created a PR against Swift 2.2 for this: https://github.com/apple/swift/pull/1798
            Hide
            ccal Cal S added a comment - - edited

            +1 that the issue can be avoided by calling self/super.init(); before retuning nil.

            Show
            ccal Cal S added a comment - - edited +1 that the issue can be avoided by calling self/super.init(); before retuning nil.
            Hide
            mjtsai Michael Tsai added a comment -

            I am also seeing this issue with a non-failable but throwing convenience initializer in an NSData extension. In this case, calling self.init before throwing did not prevent the crash. However, when I put all the code (which includes some closures and a while loop) into a helper function that I call from the initializer, the self.init workaround worked.

            Show
            mjtsai Michael Tsai added a comment - I am also seeing this issue with a non-failable but throwing convenience initializer in an NSData extension. In this case, calling self.init before throwing did not prevent the crash. However, when I put all the code (which includes some closures and a while loop) into a helper function that I call from the initializer, the self.init workaround worked.
            Hide
            DougGregor Doug Gregor added a comment -

            Michael, that's almost certainly the same issue: any kind of early exit from the initializer of an Objective-C-rooted class will run into this crash in the runtime.

            Show
            DougGregor Doug Gregor added a comment - Michael, that's almost certainly the same issue: any kind of early exit from the initializer of an Objective-C-rooted class will run into this crash in the runtime.
            Hide
            jrose Jordan Rose added a comment -

            Doug's 2.2.1 PR was accepted.

            Show
            jrose Jordan Rose added a comment - Doug's 2.2.1 PR was accepted.

              People

              • Assignee:
                slavapestov Slava Pestov
                Reporter:
                vlas Vlas Voloshin
              • Votes:
                9 Vote for this issue
                Watchers:
                18 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: