解决实例之间的循环强引用
如果两个类实例互相持有对方的强引用(如class1中的某个属性是class2,class2中的某个属性是class1),因而每个实例都让对方一直存在,这就是所谓的循环强引用。
Swift 提供了两种办法用来解决你在使用类的属性时所遇到的循环强引用问题:弱引用(weak reference)和无主引用(unowned reference)。
弱引用和无主引用允许循环引用中的一个实例引用另外一个实例而不保持强引用。这样实例能够互相引用而不产生循环强引用。
基本策略
- 对于生命周期中会变为nil的实例使用弱引用。
- 对于初始化赋值后再也不会被赋值为nil的实例,使用无主引用。
- 第三种场景,在这种场景中,两个属性都必须有值,并且初始化完成后永远不会为nil。在这种场景中,需要一个类使用无主属性,而另外一个类使用隐式解析可选属性(property!)。
解决闭包引起的循环强引用
循环强引用还会发生在当你将一个闭包赋值给类实例的某个属性,并且这个闭包体中又使用了这个类实例时。这个闭包体中可能访问了实例的某个属性,例如self.someProperty,或者闭包中调用了实例的某个方法,例如self.someMethod()。这两种情况都导致了闭包“捕获”self,从而产生了循环强引用。
解决闭包引起的循环强引用
在定义闭包时同时定义捕获列表作为闭包的一部分,通过这种方式可以解决闭包和类实例之间的循环强引用
注意Swift 有如下要求:只要在闭包内使用self的成员,就要用self.someProperty或者self.someMethod()(而不只是someProperty或someMethod())。这提醒你可能会一不小心就捕获了self。
如果闭包有参数列表和返回类型,把捕获列表放在它们前面:
lazy var someClosure: (Int, String) -> String = {
//[ ... ]中为捕获列表
[unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
// 这里是闭包的函数体
}
在闭包中使用unowned,weak的原则:
- 在闭包和捕获的实例总是互相引用并且总是同时销毁时,将闭包内的捕获定义为无主引用。
- 相反的,在被捕获的引用可能会变为nil时,将闭包内的捕获定义为弱引用。
- 如果被捕获的引用绝对不会变为nil,应该用无主引用,而不是弱引用。