关于如何使用捕获列表避免参考周期的困惑

时间:2016-04-25 05:19:08

标签: swift closures retain-cycle

我的自定义UIViewController子类有一个存储的闭包属性。闭包签名被定义为采用相同类型的类的单个参数:

class MyViewController {

    var completionHandler : ((MyViewController)->(Void))?

    // ...
}

......想法是,对象将自己作为处理程序的参数传递回来,有点like the UIAlertAction initializer

另外,为方便起见,我有一个工厂(-ish)类方法:

class func presentInstance(withCompletionHandler handler:((MyViewController)->(Void)))
{
   // ...
}

...执行以下操作:

  1. 创建视图控制器的实例
  2. 将完成处理程序分配给属性
  3. 从调用时的顶级/根视图控制器的模态显示它。
  4. 我的视图控制器肯定在泄漏:我在deinit()上设置了一个断点,但执行从未命中过,即使在我完成了我的视图控制器之后也是如此驳回。

    我不确定应该如何或在何处指定捕获列表以避免循环。我遇到的每个例子似乎都把它放在定义闭包体的地方,但是我无法编译我的代码。

    1. 我在哪里声明了闭包属性? (工作原理)

      var completionHandler : ((MyViewController)->(Void))?
      // If so, where does it go?
      
    2. 我在哪里声明了闭包参数?

      class func presentInstance(withCompletionHandler handler:((MyViewController)->(Void)))
      {
      // Again, where could it go?
      
    3. 我在哪里调用上面的函数并传递闭包体?

      MyViewController.presentInstance(withCompletionHandler:{
          [unowned viewController] viewController in 
      
          // ...
      })
      // ^ Use of unresolved identifier viewController
      // ^ Definition conflicts with previous value
      
    4. 我实际上调用关闭self? 这些都不会编译:

      self.completionHandler?(unowned self)
      self.completionHandler?([unowned self] self)
      self.completionHandler?([unowned self], self)
      

1 个答案:

答案 0 :(得分:0)

嗯,事实证明我的视图控制器被一个块保留了,但不是我想的那个:

class MyViewController
{
    deinit(){
        print("Deinit...")
    }

    // ...

    @IBAction func cancel(sender:UIButton)
    {
        completionHandler(self) 
        // View controller is dismissed, AND
        // deinit() gets called.     
    }

    @IBAction func punchIt(sender: UIButton)
    {
        MyWebService.sharedInstance.sendRequest(    
            completion:{ error:NSError? in

                 self.completionHandler(self)  
                 // View controller is dismissed, BUT
                 // deinit() does NOT get called. 
            }
        )
    }

...所以传递给MyWebService.sharedInstance.sendRequest()的闭包让我的视图控制器活着。我通过添加这样的捕获列表来修复它:

MyWebService.sharedInstance.sendRequest(    
            completion:{ [unowned self] error:NSError? in

然而,我仍然不太明白为什么传递给Web服务类的短期完成处理程序执行一次并处置掉,这使得我的视图控制器保持活动状态。那个关闭,不作为财产存储在任何地方,应该在退出后立即解除分配,对吗?

我一定错过了什么。我想我仍然没有完全考虑门户闭包。