SwiftSwiftswift

扒一扒swift中的unowned和weak下

2016-08-29  本文已影响2627人  2e919d99a871

上一篇写到了swift中引起Reference cycle(循环引用)的原因,和一些不同场景下处理的办法,那这篇我就来叨叨下平时项目里会经常遇到的Closuer(闭包)里引用self对象的处理办法

先看个例子

class HTMLElment {
    let name: String 
    let text: String? 
    var asHTML: Void -> String = { 
    // WRONG SYNTAX!!!
    if let text = self.text { 
        return "<\(self.name)>\(self.text)"
    } else {
       return "<\(self.name)>" 
    }
 } // Omit for simplicity...
}

注: lazy可以确保一个成员只能在类对象在完整初始化过以后,才能使用

定义了asHTML之后,我们就可以观察h1的构建和释放过程了。首先,我们看使用asHTML之前:

var h1: HTMLElment? = HTMLElment(name: "h1", text: "Title")h1 = nil

在Playground结果里,我们可以看到h1先被创建,而后被销毁的过程(因为HTMLElement的deinit方法被调用了)。



而当我们在让h1等于nil前,使用asHTML的话,情况就不同了:

var h1: HTMLElment? = HTMLElment(name: "h1", text: "Title")
h1.asHTML
h1 = nil

这时我们就发现,HTMLElement的deinit不再被调用了。



根据之前说过的,我们推断,一定是某处发生了循环引用了。



h1是我们定义的strong reference。Closure作为一个引用类型,它有自己的对象,因此asHTML也是一个strong reference。由于asHTML“捕获”了HTMLElement的self,因此HTMLElement的引用计数是2。当h1为nil时,asHTML对closure的引用和closure对self的“捕获”就形成了一个reference cycle。

注: 尽管在closure内部,使用了多次self,closure对self的捕获仅发生1次(引用计数只加1)。

Closure中发生reference cycle的解决办法:

本质上来说,closure作为一个引用类型,解决reference cycle的方式和解决类对象之间的reference cycle是一样的,如果引起reference cycle的"捕获"不能为nil,就把它定义为unowned,否则,定义为weak。而指定“捕获”方式的地方,叫做closure的capture list。
我们把asHTML修改成下面这样:

class HTMLElment {
   let name: String 
   let text: String? 
   lazy var asHTML: Void -> String = { 
   // text
   // Capture list 
   [unowned self] in
   if let text = self.text { 
       return "<\(self.name)>\(self.text)"
   } else {
   return "<\(self.name)>" 
   }
  }
 }

我们使用一对 [] 表示closure的capture list,由于“捕获”到的self不能为nil(否则closure也不存在了),因此我们把它定义为unowned self。在我们这样做之后,当h1为nil时,对象之间的关系就变成了这样:



由于HTMLElement没有了strong reference,因此它会被ARC释放掉,进而asHTML引用的closure也会变成“孤魂野鬼”,ARC当然也不会放过它。因此,closure和类对象间的循环引用问题就解决了。

class HTMLElment {
   let name: String
   let text: String? 
   lazy var asHTML: Void -> String = { 
   // Capture list
   [unowned self /*, other capture member*/] () -> String in
   if let text = self.text { 
     return "<\(self.name)>\(self.text)" 
   } else {
     return "<\(self.name)>" 
   }
   }
}

在利用Capture list解决了Closure中循环引用的问题以后,还要注意一点: 如果在某个Clusure中捕获到到self可以为nil,那我们就要回到上一篇开头所讲的那样,判断一下self,然后再进行处理了

{[weak self] in 
    if let weSelf = self{ 执行代码}
}

好了,以上就是我总结的所有关于swfit中Reference cycle 的知识了!~

上一篇下一篇

猜你喜欢

热点阅读