闭包产生的强引用环
发布时间:2020-12-14 06:43:47 所属栏目:百科 来源:网络整理
导读:闭包产生的强引用环 前面我们看到了强引用环是如何产生的,还知道了如何引入弱引用和无主引用来打破引用环。 将一个闭包赋值给类实例的某个属性,并且这个闭包使用了实例,这样也会产生强引用环。这个闭包可能访问了实例的某个属性,例如self.someProperty,
闭包产生的强引用环前面我们看到了强引用环是如何产生的,还知道了如何引入弱引用和无主引用来打破引用环。将一个闭包赋值给类实例的某个属性,并且这个闭包使用了实例,这样也会产生强引用环。这个闭包可能访问了实例的某个属性,例如self.someProperty,或者调用了实例的某个方法,例如self.someMethod。这两种情况都导致了闭包使用self,从而产生了抢引用环。 因为诸如类这样的闭包是引用类型,导致了强引用环。当你把一个闭包赋值给某个属性时,你也把一个引用赋值给了这个闭包。实质上,这个之前描述的问题是一样的-两个强引用让彼此一直有效。但是,和两个类实例不同,这次一个是类实例,另一个是闭包。 Swift提供了一种优雅的方法来解决这个问题,我们称之为闭包占用列表(closuer capture list)。同样的,在学习如何避免因闭包占用列表产生强引用环之前,先来看看这个抢引用环是如何产生的。 下面的例子将会告诉你当一个闭包引用了self后是如何产生一个抢引用环的。本例顶一个一个名为HTMLElement的类,来建模HTML中的一个单独的元素:
除了上面的两个属性,HTMLElement还定义了一个lazy属性asHTML。这个属性引用了一个闭包,将name和text组合成HTML字符串片段。该属性是() -> String类型,就是“没有参数,返回String的函数”。 默认将闭包赋值给了asHTML属性,这个闭包返回一个代表HTML标签的字符串。如果text值存在,该标签就包含可选值text;或者不包含文本。对于段落,根据text是"some text"还是nil,闭包会返回"<p>some text</p>"或者"<p />"。 可以像实例方法那样去命名、使用asHTML。然而,因为asHTML终究是闭包而不是实例方法,如果你像改变特定元素的HTML处理的话,可以用定制的闭包来取代默认值。 注意:asHTML声明为lazy属性,因为只有当元素确实需要处理为HTML输出的字符串时,才需要使用asHTML。也就是说,在默认的闭包中可以使用self,因为只有当初始化完成以及self确实存在后,才能访问lazy属性。HTMLElement只有一个初始化函数,根据name和text(如果有的话)参数来初始化一个元素。该类也定义了一个deinitializer,当HTMLElement实例被销毁时,打印一条消息。 下面的代码创建一个HTMLElement实例并打印消息。 var paragraph: HTMLElement? = HTMLElement(name: "p",text: "hello,world")
注意上面的paragraph变量定义为可选HTMLElement,因此我们可以赋值nil给它来演示强引用环。不幸的是,HTMLElement类产生了类实例和asHTML默认值的闭包之间的强引用环。如下图所示: 实例的asHTML属性持有闭包的强引用。 但是,闭包使用了self(引用了self.name和self.text),因此闭包占有了self,这意味着闭包又反过来持有了HTMLElement实例的强引用。这样就产生了强引用环。(更多闭包哪占有值的信息,请参考Capturing Values)。 注意:虽然闭包多次使用了self,它只占有HTMLElement实例的一个强引用。如果设置paragraph为nil,打破它持有的HTMLElement实例的强引用,HTMLElement实例和它的闭包都不会被销毁,就因为强引用环: paragraph = nil
解决闭包产生的强引用环在定义闭包时同时定义占有列表作为闭包的一部分,可以解决闭包和类实例之间的强引用环。占有列表定义了闭包内占有一个或者多个引用类型的规则。和解决两个类实例间的强引用环一样,声明每个占有的引用为弱引用或无主引用,而不是强引用。根据代码关系来决定使用弱引用还是无主引用。注意:Swift有如下约束:只要在闭包内使用self的成员,就要用self.someProperty或者self.someMethod(而非只是someProperty或someMethod)。这可以提醒你可能会不小心就占有了self。 定义占有列表占有列表中的每个元素都是由weak或者unowned关键字和实例的引用(如self或someInstance)组成。每一对都在花括号中,通过逗号分开。占有列表放置在闭包参数列表和返回类型之前: @lazy var someClosure: (Int,String) -> String = {
@lazy var someClosure: () -> String = {
弱引用和无主引用当闭包和占有的实例总是互相引用时并且总是同时销毁时,将闭包内的占有定义为无主引用。相反的,当占有引用有时可能会是nil时,将闭包内的占有定义为弱引用。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为nil。利用这个特性,我们可以在闭包内检查他们是否存在。 注意:如果占有的引用绝对不会置为nil,应该用无主引用,而不是弱引用。前面提到的HTMLElement例子中,无主引用是正确的解决强引用的方法。这样编码HTMLElement类来避免强引用环: 上面的HTMLElement实现和之前的实现相同,只是多了占有列表。这里,占有列表是[unowned self],代表“用无主引用而不是强引用来占有self”。 和之前一样,我们可以创建并打印HTMLElement实例: println(paragraph!.asTHML())
这一次,闭包以无主引用的形式占有self,并不会持有HTMLElement实例的强引用。如果赋值paragraph为nil,HTMLElement实例将会被销毁,并能看到它的deinitializer打印的消息。 paragraph = nil
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |