第二十四章 自动引用计数
6. Resolving Strong Reference Cycles for Closure ( 解决闭包引起的强引用循环 )
我们可以定义一个捕获列表来作为闭包的一部分的方式来解决闭包和类实例之间引起的强引用循环,捕获列表定义了一些使用的规则,当在闭包体里面捕获到一个或多个引用循环的时候。作为一个两个实例之间的强引用循环,我们可以每一个捕获到的引用来当作一个弱引用或无主引用而不是一个强引用。适当地选择弱引用或无主引用取决于我们代码在不同位置的关系来决定的。
6.1 Defining a Capture List (定义捕获列表)
捕获列表里的每一个物品都由元素组成的,一个成堆出现且带有weak或unowned关键字元素。另一个元素是类实例的引用(self),初始化过后的变量(delegate = self.delegate
),这些项都写在一对方括号里面并用逗号隔开。
在闭包参数列表和返回类型的前面添加捕获列表
lazy var someClosure = {
[unowned self, weak delegate = self.delegate]
(index: Int, stringToProcess: String) -> String in
// closure body goes here
}
如果一个闭包并没有指明参数列表或返回类型,是因为该参数和类型可以在上下问中被推断出来,这样我们就要在闭包的最开头添加捕获列表和后跟in
关键字
lazy var someClosure = {
[unowned self, weak delegate = self.delegate] in
// closure body goes here
}
6.2 Weak and Unowned Reference ( 弱引用和无主引用)
在闭包和捕获的实例总是互相引用并且总是同时释放的时候,可以将在闭包中捕获到的参数作为无主引用使用。相反的,在捕获的引用可能为nil
的时候,将闭包捕获的参数作为弱引用使用,弱引用总是可选的类型,并且会自动变成nil
只有当引用的实例被释放时,这就会使我们在闭包体里来检查该实例的引用是否存在。
无主引用是一个比较合适的捕获方法来解决上章节中闭包中的强引用循环HTMLElement
例子中的强引用循环,下面是如何用写这个HTMLElement类来避免强引用循环。
// unowned reference to resolve strong referene cycle for closure
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
[unowned self] in
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
该HTMLElement的实现和之前的实现是一样的。除了在asHTML闭包里添加了一个捕获列表。在这个案例里,捕获列表是[unowned self]
,这就意味着将self捕获为无主引用而不是强引用。所以我们可以像之前那样创建和打印HTMLElement实例。
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
// Prints "<p>hello, world</p>"
下面是使用了捕获列表后的引用图例。
此时此刻,被闭包捕获到的self
是无主引用,并且不会强烈保留它捕获到的HTMLElement实例,如果我们将强引用中的变量paragraph设置为nil。HTMLElement实例将会被释放,所以通过析构器输出的文本信息我们可以看出该实例被释放了。
paragraph = nil
// Prints "p is being deinitialized"