在可能的块/闭包保留循环中出现了swift_unknownWeakRelease()。

4

我有一个视图控制器,在我的应用程序启动时显示,以准备使用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之间存在强引用循环,因为那是我能想到潜在发生的唯一地方。如果我将捕获列表从[未拥有self]更改为[弱self]或完全删除它(在闭包内保留对self的强引用),程序就可以继续执行。当我在iOS 8的iPhone 5模拟器或iOS 7.1的5S模拟器上运行应用程序时,这个错误不会发生。
我该如何修改代码以避免在所有运行iOS 7.0+的设备上崩溃? 我确信unowned是正确的修饰符。我期望self在completionHandler完成之前仍然存在,因此weak不适合。以下是调试导航器中的完整日志,如果有帮助:
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

如果我将捕获列表从[unowned self]更改为[weak self],那么如何处理self是可选的?您会用self!替换使用self吗,还是有其他方法? - newacct
2个回答

7
这里的问题出在未持有的引用上。当self被释放后,代码块仍然保留了对self的引用,试图调用已经为nil的self。使用weak来避免这种情况。由于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()
        }
      }
    }
  }

}

我也遇到了swift_unknownWeakRelease崩溃问题。将“unowned self”替换为“weak self”并进行nil检查解决了问题。谢谢! - 拇指 muzhi.com
2
很棒的答案。我想添加一些链接,这些链接帮助我更好地理解它:http://bit.ly/resolvingStrongReferenceCyclesForClosures(苹果的东西)和http://bit.ly/weakSelfAndStrongSelf(某个酷哥)。 - backslash-f

1
我认为保留周期不会成为问题。`prepareWithCompletionHandler`闭包参数捕获了`self`,但是`self`(`SetupViewController`的实例)并不拥有文档变量。在这种情况下,您不需要使用捕获列表。

嗯...闭包怎么办?谁拥有完成处理程序? - Matthew Quiros
闭包由 prepareWithCompletionHandler 参数变量拥有,并在 prepareWithCompletionHandler 返回之前一直被保留。 - Kirsteins
如果您不想在闭包执行结束之前保留self,您可以使用@insane-36的方法,但这里不存在保留循环的风险。 - Kirsteins

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接