【Swift】Swift循环引用总结

欢迎关注微信公众号:全栈工厂

本文主要参考

1.循环强引用

由于Swift采用ARC(引用计数)来进行内存管理,开发者不必再去手动释放内存,但是如果代码产生了循环强引用,对象的引用计数无法归零,那么系统便无法自动释放对象,从而产生内存泄漏。

1.1 对象的循环强引用

我们假设两个类 类1、类2,这两个类中都有一个类类型的存储属性:

// 代码1.1
 class 类1{
      var 对象:类2!
      init(){
        print("对象1初始化!")
      }
    deinit{
      print("对象1释放!")
    }
  }
class 类2{
  var 对象:类1!
  init(){
      print("对象2初始化!")
  }
  deinit{
        print("对象2释放!")
  }
 }

然后我们对着两个类分别实例化两个对象,并让这两个对象彼此互相持有:

// 代码1.2
 var 对象1:类1?  = 类1()
 var 对象2:类2? =类2()
 对象1?.对象 = 对象2
 对象2?.对象 = 对象1

 对象1 = nil
 对象2 = nil

这样就会造成一个对象间的循环引用。最后,我们将这两个对象进行释放。发现控制台输出结果为:

对象1初始化!
对象2初始化!

而这两个对象并没有被释放,造成内存泄漏!

1.2 闭包的循环强引用

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

// 代码1.3
class 类3{
 let name = "老王"
 lazy var 闭包:(Void -> Void)? = {
    var name1 = self.name
    var name2 = self.getName()
    print(name1)
    print(name2)
  }
  init(){
    print("对象3初始化!")
  }
  func getName()->String{
      return "老王"
  }
  deinit{
     print("对象3释放!")
  }
}

var 对象3:类3? = 类3()
对象3.闭包!()

对象3 = nil

在类3中,由于闭包中调用了实例的属性以及方法,从而导致闭包捕获了self,产生了循环强引用,造成了内存泄漏。

2.如何避免循环强引用

Swift中防止出现循环强引用主要有两种方法:
1. 代码显式调用,打破循环强引用
2. 使用weak、unowned关键字,用弱引用或无主引用打破循环强引用

2.1代码显式调用,打破循环强引用

这种方法需要开发者在使用完对象或闭包后,主动释放所持有的其他对象的强引用,从而打破循环,让系统回收内存。
例如,我们可以将代码1.2修改为:

// 代码2.1
 var 对象1:类1?  = 类1()
 var 对象2:类2? =类2()
 对象1?.对象 = 对象2
 对象2?.对象 = 对象1

//主动释放所持有的强引用对象
 对象1?.对象 = nil
 对象2?.对象 = nil
 对象1 = nil
 对象2 = nil

对于代码1.3可以主动释放对象中的闭包:

//代码2.2
var 对象3:类3? = 类3()
对象3?.闭包!()

对象3?.闭包 = nil
对象3 = nil

这样就可以避免内存泄漏,但是这种方式有悖于ARC设计的初衷,不推荐使用。

2.2使用weak、unowned关键字,用弱引用或无主引用打破循环强引用

2.2.1 weak、unowned关键字

Swift中,声明属性或者变量时,在前面加上weak关键字表明这是一个弱引用,弱引用不会对其引用的实例保持强引用,不会增加引用对象的引用计数,因而不会阻止 ARC 销毁被引用的实例。因为弱引用可以没有值,因此你必须将每一个弱引用声明为可选类型。弱引用不会保持所引用的实例,即使引用存在,实例也有可能被销毁。因此,ARC 会在引用的实例被销毁后自动将其赋值为nil。你可以像其他可选值一样,检查弱引用的值是否存在,你将永远不会访问已销毁的实例的引用。

与weak关键字类似,在属性或变量前面加上unowned关键字表明这是一个无主引用,无主引用也不会对其引用的实例保持强引用,从而避免出现循环强引用。然而与weak关键字不同的是,即使引用的对象已经被销毁,无主引用仍然存在,这时候如果强制使用引用会导致程序的崩溃。对于这两个关键字的选择,建议如果能够确保在引用的使用过程中对象始终存在,则尽量使用unowned,否则建议使用更为安全的weak关键字。

2.2.2 weak、unowned关键字在属性或变量上的使用

weak、unowned关键字在属性或变量上的使用比较简单,代码如下:

// 代码2.3
//weak关键字
class 类1{
  weak var 对象:类2!
  init(){
     print("对象1 初始化!")
  }
  deinit{
    print("对象1 释放!")
  }
 }
// unowned关键字
class 类1{
  unowned var 对象:类2
  init(对象2:类2){
      对象 = 对象2
      print("对象1 初始化!")
  }
  deinit{
      print("对象1 释放!")
  }  
}

2.2.3 weak、unowned关键字在闭包中的使用

weak、unowned关键字在闭包中的使用主要有两种方式,首先便是在闭包定义之前先用weak、unowned关键字引用对象,从而让闭包捕获弱引用或无主引用对象,从而保证不会出现循环强引用:

class 类3{
  let name = "老王"
  var closure:(Void -> Void)?
  init(){
    print("对象3初始化!")
    unowned let myself = self
    closure = {
        //捕获myself无主引用
        var name = myself.name
        name = myself.getName()
        print(name)
    }
  }
 }

这种方式比较麻烦,不推荐使用。Swift提供了另外一个简便的方式:自定义捕获列表。开发者在定义闭包的时候可以首先定义需要捕获变量类型为weak或unowned,从而防止出现循环强引用:

class 类3{
  let name = "老王"
  lazy var closure:(Void -> Void)? = {
  //自定义捕获列表
   [unowned self] in
      var name = self.name
      name = self.getName()
      print(name)
   }
 }

这样,闭包中再使用self的时候便不会出现强引用了。

注:文中如有任何错误,请各位批评指正!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容