Swift - 在闭包中强烈捕获 self 后预计会发生泄漏

Swift - Expecting a leak after strongly capturing self in closure

任何人都可以解释为什么这不会泄漏吗?

我在 closure 中捕获 self,所以我会有两个指向彼此的强指针,因此,永远不应该调用 deinit 消息人物对象。

首先,这是我的class

class Person {
    var name: String
    init(name: String) { self.name = name }
    deinit { print("\(name) is being deinitialized") }
}

这是我的 ViewController 的实现:

class ViewController: UIViewController {

    var john:Person?

    func callClosureFunction( closure:(name:Bool) -> () ) {
        closure(name: true)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        john = Person(name:"John")

        self.callClosureFunction { (name) in

            self.john?.name = "John Appleseed"
            self.john = nil

            // xcode prints - John Appleseed is being deinitialized
        }

    }

}

我希望能够通过以下方式解决问题:

self.callClosureFunction { [weak self] (name) in ...

但这甚至没有必要。为什么?

您正在捕获指向 ViewControllerself,但您想知道 Person 实例。

Person 实际上不是循环引用,因此当您在闭包结束时将其设置为 nil 时,它会被取消初始化并释放。

ViewController 实施 deinit,看看它是如何工作的。

由于您的视图控制器未保留 闭包,因此不存在循环引用。如果你这样写:

class ViewController: UIViewController {

    var john:Person?
    var closure:(Bool)->()? 

    func callClosureFunction( closure:((name:Bool) -> ())? ) {
        closure?(name: true)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        john = Person(name:"John")
        closure = { (name) in

            self.john?.name = "John Appleseed"    

            // Because this closure will never be released, the instance of Person will never deinit either
        }
        self.callClosureFunction(closure) 
    }  
}

然后视图控制器将保留闭包,闭包将通过其对 self 的引用保留视图控制器。因此,两者都不会被释放,并且如果您没有明确设置 self.john = nil(您在原始示例中所做的),那么 Person 实例将永远不会被调用 deninit

在不必要时在闭包中不恰当地使用 weak self 是很常见的(这实际上会导致一些不明显的错误)。要记住的关键规则是弱引用通常不是 ARC 下的默认值。 Strong 应该是默认值 除非它会导致保留循环 ,在这种情况下,weak 应该只用于打破循环引用。闭包也一样:强 self 应该是默认值,除非 在这种情况下 self 也对闭包本身有强引用。

I'm capturing self within a closure so I would have two strong pointers pointing at each other, therefore, the deinit message shouldn't ever be called for the Person object.

不,你有一个强指针,从闭包到self。没有从闭包返回到 self 的循环引用。因此,您有一个 directed acylic graph,这对 ARC 来说没有问题。

但是,您的实验从一开始就存在缺陷。即使捕获了闭包,John Appleseed Person 对象仍然会 deinit。该对象的生命周期完全取决于来自您的 ViewControllerjohn 引用。当您将该引用设置为 nil 时,您将删除对 John Appleseed 对象的最后一个引用,因此它被取消初始化。