发布于 2015-06-14 01:44:29 | 272 次阅读 | 评论: 0 | 来源: 网络整理
在定义闭包时同时定义捕获列表作为闭包的一部分,通过这种方式可以解决闭包和类实例之间的循环 强引用。捕获列表定义了闭包体内捕获一个或者多个引用类型的规则。跟解决两个类实例间的循环强引用一样,声明每个捕获的引用为弱引用或无主引用,而不是强 引用。应当根据代码关系来决定使用弱引用还是无主引用。
注意:
Swift 有如下要求:只要在闭包内使用self
的成员,就要用self.someProperty
或者self.someMethod
(而不只是someProperty
或someMethod
)。这提醒你可能会不小心就捕获了self
。
捕获列表中的每个元素都是由weak
或者unowned
关键字和实例的引用(如self
或someInstance
)成对组成。每一对都在方括号中,通过逗号分开。
捕获列表放置在闭包参数列表和返回类型之前:
@lazy var someClosure: (Int, String) -> String = {
[unowned self] (index: Int, stringToProcess: String) -> String in
// closure body goes here
}
如果闭包没有指定参数列表或者返回类型,则可以通过上下文推断,那么可以捕获列表放在闭包开始的地方,跟着是关键字in
:
@lazy var someClosure: () -> String = {
[unowned self] in
// closure body goes here
}
当闭包和捕获的实例总是互相引用时并且总是同时销毁时,将闭包内的捕获定义为无主引用。
相反的,当捕获引用有时可能会是nil
时,将闭包内的捕获定义为弱引用。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为nil
。这使我们可以在闭包内检查它们是否存在。
注意:
如果捕获的引用绝对不会置为nil
,应该用无主引用,而不是弱引用。
前面的HTMLElement
例子中,无主引用是正确的解决循环强引用的方法。这样编写HTMLElement
类来避免循环强引用:
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 {
println("(name) is being deinitialized")
}
}
上面的HTMLElement
实现和之前的实现一致,只是在asHTML
闭包中多了一个捕获列表。这里,捕获列表是[unowned self]
,表示“用无主引用而不是强引用来捕获self
”。
和之前一样,我们可以创建并打印HTMLElement
实例:
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
println(paragraph!.asHTML())
// prints "<p>hello, world</p>"
使用捕获列表后引用关系如下图所示:
这一次,闭包以无主引用的形式捕获self
,并不会持有HTMLElement
实例的强引用。如果将paragraph
赋值为nil
,HTMLElement
实例将会被销毁,并能看到它的析构函数打印出的消息。
paragraph = nil
// prints "p is being deinitialized"