美文网首页征服iOS
Swift解决【闭包引起的循环强引用】

Swift解决【闭包引起的循环强引用】

作者: piggybear | 来源:发表于2017-03-15 13:14 被阅读791次

    循环强引用还会发生在当你将一个闭包赋值给类实例的某个属性,并且这个闭包体中又使用了这个类实例时。这个闭包体中可能访问了实例的某个属性,例如self.someProperty,或者闭包中调用了实例的某个方法,例如self.someMethod()。这两种情况都导致了闭包“捕获”self,从而产生了循环强引用。

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

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

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

    定义捕获列表

    捕获列表中的每一项都由一对元素组成,一个元素是weakunowned关键字,另一个元素是类实例的引用(例如self)或初始化过的变量(如delegate = self.delegate!)。这些项在方括号中用逗号分开。

    如果闭包有参数列表和返回类型,把捕获列表放在它们前面:

    lazy var someClosure: (Int, String) -> String = {
        [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
        // 这里是闭包的函数体
    }
    

    如果闭包没有指明参数列表或者返回类型,即它们会通过上下文推断,那么可以把捕获列表和关键字in放在闭包最开始的地方:

    lazy var someClosure: Void -> String = {
        [unowned self, weak delegate = self.delegate!] in
        // 这里是闭包的函数体
    }
    
    弱引用和无主引用

    在闭包和捕获的实例总是互相引用并且总是同时销毁时,将闭包内的捕获定义为无主引用

    相反的,在被捕获的引用可能会变为nil时,将闭包内的捕获定义为弱引用。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为nil。这使我们可以在闭包体内检查它们是否存在。

    注意
    如果被捕获的引用绝对不会变为nil,应该用无主引用,而不是弱引用。

    案例

    class HTMLElement {
    
        let name: String
        let text: String?
    
        lazy var asHTML: Void -> 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")
        }
    
    }
    

    捕获列表是[unowned self],表示“将self捕获为无主引用而不是强引用”。

    创建并打印HTMLElement实例:

    var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
    print(paragraph!.asHTML())
    // 打印 “<p>hello, world</p>”
    

    使用捕获列表后引用关系如下图所示:


    这一次,闭包以无主引用的形式捕获self,并不会持有HTMLElement实例的强引用。如果将paragraph赋值为nilHTMLElement实例将会被销毁,并能看到它的析构函数打印出的消息:
    paragraph = nil
    // 打印 “p is being deinitialized”
    

    引自:http://www.piggybear.net/?p=690

    相关文章

      网友评论

        本文标题: Swift解决【闭包引起的循环强引用】

        本文链接:https://www.haomeiwen.com/subject/viylnttx.html