发布于 2015-06-14 01:44:29 | 263 次阅读 | 评论: 0 | 来源: 网络整理

解决闭包引起的循环强引用

在定义闭包时同时定义捕获列表作为闭包的一部分,通过这种方式可以解决闭包和类实例之间的循环 强引用。捕获列表定义了闭包体内捕获一个或者多个引用类型的规则。跟解决两个类实例间的循环强引用一样,声明每个捕获的引用为弱引用或无主引用,而不是强 引用。应当根据代码关系来决定使用弱引用还是无主引用。


注意:
Swift 有如下要求:只要在闭包内使用self的成员,就要用self.someProperty或者self.someMethod(而不只是somePropertysomeMethod)。这提醒你可能会不小心就捕获了self
 

定义捕获列表

捕获列表中的每个元素都是由weak或者unowned关键字和实例的引用(如selfsomeInstance)成对组成。每一对都在方括号中,通过逗号分开。

捕获列表放置在闭包参数列表和返回类型之前:


@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赋值为nilHTMLElement实例将会被销毁,并能看到它的析构函数打印出的消息。


paragraph = nil
// prints "p is being deinitialized"
最新网友评论  共有(0)条评论 发布评论 返回顶部

Copyright © 2007-2017 PHPERZ.COM All Rights Reserved   冀ICP备14009818号  版权声明  广告服务