5

我有一个保留的循环,所以我的视图控制器的 deinit 不会被调用,我正在尝试通过添加 [unowned self] 来解决这个问题,但我不太确定在我的案例中将 unowned 放在哪里:

情况1

class YADetailiViewController: UIViewController {
 var subscription: Subscription<YAEvent>?

 override func viewDidLoad() {
    super.viewDidLoad()
    if let query = self.event.subscribeQuery() {
        self.subscription = Client.shared.subscribe(query)
        self.subscription?.handle(Event.updated) {
            query, object in
            DispatchQueue.main.async {
                [unowned self] in// Put unowned here won't break the cycle, but it does compile and run 
                self.pageViewLabel.text = String(object.pageViews) + " VIEW" + ((object.pageViews > 1) ? "S" : "")
            }
        }
    }
 }
}

案例2

 override func viewDidLoad() {
    super.viewDidLoad()
    if let query = self.event.subscribeQuery() {
        self.subscription = Client.shared.subscribe(query)
        self.subscription?.handle(Event.updated) {
            [unowned self] query, object in // Put unowned breaks the cycle, and deinit is called
                DispatchQueue.main.async { 
                    self.pageViewLabel.text = String(object.pageViews) + " VIEW" + ((object.pageViews > 1) ? "S" : "")
                }
            }
        }
    }

我很好奇这两种情况之间有什么区别以及为什么一种有效而另一种无效

4

1 回答 1

1

实际上,正如@matt 正确提到的,问题与self捕获的时间有关。事实上,这段代码self被捕获了两次:

  • 当外部闭包传递给handle方法时
  • 当内部闭包被传递给async方法时(在handle闭包执行期间)

外部闭包需要self将其传递给内部闭包,否则内部闭包将无法捕获它。

性质或保留周期如下:self(YADetailiViewController) -> subscription-> 闭包(handle参数)-> self。要打破这个循环,不要保留self在那个(外部)封闭中就足够了。这就是第二个代码示例有效的原因。

(在内部闭包中)的第二次捕获self仅在执行外部闭包时发生并持续到async执行该块 - 通常它的时间很短。

在第一个代码示例中,您没有打破循环。第二次捕获self没有发生,但第一次捕获(导致你的循环)仍然存在。

如果handle在视图控制器已经取消/释放时仍然可以调用您的闭包,那么按照@AdrianBobrowski 的建议,您应该使用weak而不是unowned防止可能的崩溃。

于 2018-02-12T07:36:21.033 回答