在可能的块/闭包保留周期中获取swift_unknownWeakRelease()

时间:2014-09-30 07:44:11

标签: ios iphone cocoa-touch swift automatic-ref-counting

我有一个视图控制器,显示在我的应用程序的开头,以准备我正在使用Core Data的UIManagedDocument。问题:当我在带有iOS 7.1的iPhone 4S设备上运行应用程序时,我一直遇到swift_unknownWeakRelease()崩溃。这是代码:

class SetupViewController: UIViewController {

    @IBOutlet weak var loadingView: UIActivityIndicatorView!

    override func viewDidAppear(animated: Bool)  {
        super.viewDidAppear(animated)
        let document = SPRManagedDocument.sharedDocument()

        document.prepareWithCompletionHandler {[unowned self] success in
            if success {
                self.dismissViewControllerAnimated(false, completion: nil)
            } else {
                self.loadingView.stopAnimating()
                UIAlertView(title: "Error", message: "Categories can't be loaded.", delegate: nil, cancelButtonTitle: "OK").show()
            }
        }
    }

}

我怀疑这个区块和self之间有一个强大的参考周期,因为这是我唯一可以看到它的地方。确实如此,如果我将捕获列表从[unowned self]更改为[weak self]或完全删除它(在闭包内保持对self的强引用),程序会继续正常。 当我在iOS 8的iPhone 5模拟器或iOS 7.1的5S模拟器上运行应用程序时,不会发生此错误。

如何以不同的方式编写代码,以便在运行iOS 7.0+的所有设备上避免崩溃?我确信unowned是正确的修饰符。我希望selfcompletionHandler完成之前仍然存在,因此weak感觉不对。如果有帮助,这里是Debug Navigator中的完整日志:

Thread 1Queue : com.apple.main-thread (serial)
#0  0x0050c0ae in swift_unknownWeakRelease ()
#1  0x0012d760 in Spare.SetupViewController.(viewDidAppear (Spare.SetupViewController) -> (Swift.Bool) -> ()).(closure #1) at /Users/local.m.quiros/Development/spare-ios/Spare/View Controllers/SetupViewController.swift:28
#2  0x00108a8c in reabstraction thunk helper from @callee_owned (@unowned Swift.Bool) -> (@unowned ()) to @callee_owned (@in Swift.Bool) -> (@out ()) at /Users/local.m.quiros/Development/spare-ios/Spare/View Controllers/EditCategoryViewController.swift:180
#3  0x0012c68c in partial apply forwarder for reabstraction thunk helper from @callee_owned (@unowned Swift.Bool) -> (@unowned ()) to @callee_owned (@in Swift.Bool) -> (@out ()) ()
#4  0x00108ac4 in reabstraction thunk helper from @callee_owned (@in Swift.Bool) -> (@out ()) to @callee_owned (@unowned Swift.Bool) -> (@unowned ()) at /Users/local.m.quiros/Development/spare-ios/Spare/View Controllers/EditCategoryViewController.swift:180
#5  0x00108b18 in reabstraction thunk helper from @callee_owned (@unowned Swift.Bool) -> (@unowned ()) to @callee_unowned @objc_block (@unowned ObjectiveC.ObjCBool) -> (@unowned ()) at /Users/local.m.quiros/Development/spare-ios/Spare/View Controllers/EditCategoryViewController.swift:180
#6  0x00149e68 in __51-[SPRManagedDocument prepareWithCompletionHandler:]_block_invoke at /Users/local.m.quiros/Development/spare-ios/Spare/Objects/SPRManagedDocument.m:49
#7  0x3b4baf86 in _dispatch_barrier_sync_f_slow_invoke ()
#8  0x3b4b381e in _dispatch_client_callout ()
#9  0x3b4ba49e in _dispatch_main_queue_callback_4CF$VARIANT$mp ()
#10 0x3071b8a0 in __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ ()
#11 0x3071a174 in __CFRunLoopRun ()
#12 0x30684ebe in CFRunLoopRunSpecific ()
#13 0x30684ca2 in CFRunLoopRunInMode ()
#14 0x355de662 in GSEventRunModal ()
#15 0x32fd114c in UIApplicationMain ()
#16 0x00167060 in main at /Users/local.m.quiros/Development/spare-ios/Spare/main.m:16

2 个答案:

答案 0 :(得分:7)

这里的问题是无主参考。当自我被释放时,该块仍然保留自我,因此试图调用自己是零。使用弱以防止这种情况。由于weak是块内的可选类型,因此可以使用条件展开并执行其他代码,

class SetupViewController: UIViewController {

  @IBOutlet weak var loadingView: UIActivityIndicatorView!

  override func viewDidAppear(animated: Bool)  {
    super.viewDidAppear(animated)
    let document = SPRManagedDocument.sharedDocument()

    document.prepareWithCompletionHandler {[weak self] success in
      if let weakSelf = self{
        if success {
          weakSelf.dismissViewControllerAnimated(false, completion: nil)
        } else {
          weakSelf.loadingView.stopAnimating()
          UIAlertView(title: "Error", message: "Categories can't be loaded.", delegate: nil, cancelButtonTitle: "OK").show()
        }
      }
    }
  }

}

答案 1 :(得分:1)

我不认为保留周期可能存在问题。 prepareWithCompletionHandler闭包参数捕获self,但selfSetupViewController的实例)不拥有文档变量。在这种情况下,您不需要使用捕获列表。