首页 > 技术文章 > Swift-08-闭包引起的循环强引用

tanglimei 2016-01-15 18:27 原文

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

  循环强引用的产生,是因为闭包和类相似,都是引用类型。当你把闭包赋值给某个属性时,你也把一个引用赋值给了这个闭包。实质上,这跟之前的问题一样,两个强引用让彼此一直有效。但是,和两个类实例不同,这次一个是类实例,另一个是闭包。

  Swift提供了一种优雅的方法来解决这个问题,称之为闭包捕获列表。

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

  ----------定义捕获列表

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

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

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

  ----------弱引用和无主引用

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

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

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

class HTMLElement {
    let name:String
    let text:String?
    
    lazy var asHTML:Void -> String = {
        [unowned self] in //表示“用无主引用而不是强引用来捕获 self ”
        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")
    }
}

var paragraph:HTMLElement? = HTMLElement(name: "p", text: "hello world")
print(paragraph!.asHTML())

  打印:

<p> hello world<p>

 

这个闭包这个地方,没有看懂。

推荐阅读