具有闭包的强引用循环

kel*_*ket 1 closures memory-leaks ios strong-references swift

我正在努力了解何时需要注意强参考周期可能造成的内存泄漏.从我从swift文档中收集的内容来看,self在同一实例中声明为实例属性的闭包中使用引用将导致强引用循环,除非我声明了捕获列表,例如:

class A {

    var a: String

    lazy var aClosure: () -> () = { [unowned self] in
        println(self.a)
    }

    init(a: String) {
        self.a = a
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,没有存储为实例属性的闭包或作为其他类的实例属性存储的闭包会发生什么?在这些情况下,我是否还需要担心强大的参考周期?

Max*_*hev 7

您询问的案例不会导致参考周期.

仅当2个或更多对象直接或间接地具有指针(或由属性内的块捕获)时,才会发生引用循环:

A->B and B->A (direct)
A->B, B->C, C->A (indirect)
Run Code Online (Sandbox Code Playgroud)

现在,没有存储为实例属性的闭包会发生什么

通常,您可能会看到一个视图控制器,它调用某个库并提供处理程序块.例如:

// inside some method of view controller
APIWrapper.sharedInstance().callApiWithHandler(handler: { 
     (finished: Bool) -> Void in
    // process result of the action
    self.showResults()
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您不知道完成此操作需要多长时间.您的块可能会被提交到私有操作队列中.所有捕获的对象将保持活动状态,直到此操作完成.

现在危险部分甚至是:如果用户按下后退按钮(假设涉及导航控制器)并且当前视图控制器从导航堆栈中弹出,它将因捕获而保持活动,self即使它不会显示在屏幕上.

这应该改写为:

    // inside some method of view controller
    APIWrapper.sharedInstance().callApiWithHandler(handler: { 
         [weak self]
         (finished: Bool) -> Void in
        // process result of the action
        self?.showResults()
    }
Run Code Online (Sandbox Code Playgroud)

存储为其他类的实例属性的闭包

与此部分类似:您可能无法控制保持引用块的对象的生命周期.

捕获的对象是隐式引用,可能很难调试.

总结一下:使用块你应该总是想到这个块会存活多长时间以及它是否产生循环.使用weak/ 捕获对象是一种很好的做法,unowned除非您有充分的理由不这样做.