Swift专题讲解十六——ARC在Swift中的应用

Swift专题讲解十六——ARC在Swift中的应用

一、引言

        ARC(自动引用计数)是Objective-C和Swift中用于解决内存管理问题的方案。在学习Objective-C编程时经常会学习到一个关于ARC的例子:在一个公用的图书馆中,每次进入一人就将卡插入,走的时候将自己的卡拔出拿走。图书馆系统会判定只要有卡插入,就将图书馆的灯打开,当所有卡都被取走后,将图书馆的灯关掉。这个例子对应于Objective-C中的对象声明周期管理十分贴切。每当一个对象增加一个引用时,其引用计数会加1,当一个引用被取消时,对象的引用计数减1,当引用计数减为0时,说明此对象将不再有任何引用,对象会被释放掉,让出内存。Swift也采用同样的方式进行内存管理。

        注意:在Swift中只有引用类型有自动引用计数,结构体、枚举这类值类型是没有引用计数的。关于引用计数的示例代码如下:

1class MyClass { 2 deinit{ 3 print("MyClass deinit") 4 } 5} 6var cls1:MyClass? = MyClass() 7var cls2:MyClass? = cls1 8var cls3:MyClass? = cls2 9cls2 = nil 10cls1 = nil 11//执行下面代码后才会打印“MyClass deinit” 12cls3 = nil

二、循环引用的处理方法

        在开发中,开发者一不小心就会写出产生循环引用的代码,在上面的示例中可以看出,除非实例的引用全部解除,否则实例将不会调用析构方法,内存不会被释放,如果在写代码时,A引用了B,同样B也引用了A,那么实际上现在A和B的引用计数都是2,将A和B都置为nil后,A和B实例依然保有1个引用计数,都不会被释放,实例如下:

1class MyClassOne { 2 var cls:MyClassTwo? 3 deinit{ 4 print("ClassOne deinit") 5 } 6} 7class MyClassTwo { 8 var cls:MyClassOne? 9 deinit{ 10 print("ClassTwo deinit") 11 } 12} 13var obj1:MyClassOne? = MyClassOne() 14var obj2:MyClassTwo? = MyClassTwo() 15obj1?.cls = obj2 16obj2?.cls = obj1 17obj1=nil 18obj2=nil 19//没有打印析构函数的调用信息

对于上面的情况,可以将属性声明称weak类型来防止这种循环引用,weak的作用在于只是弱引用实例,原实例的引用计数并不会加1,示例如下:

1//关于弱引用的演示 2class MyClassThree{ 3 weak var cls:MyClassFour? 4 deinit{ 5 print("ClassThree deinit") 6 } 7} 8class MyClassFour { 9 var cls:MyClassThree? 10 deinit{ 11 print("ClassFour deinit") 12 } 13} 14var obj3:MyClassThree? = MyClassThree() 15var obj4:MyClassFour? = MyClassFour() 16obj3?.cls = obj4 17obj4?.cls = obj3 18obj4=nil 19//此时obj3中的cls也为nil 20obj3?.cls

若引用的实例被释放后,其在另一个实例中的引用也将被置为nil,所以weak只能用于optional类型的属性,然而在开发中还有一种情况,某个类必须保有另一个类的示例,这个实例不能为nil,但是这个属性又不能影响其原始实例的释放,这种情况也会造成循环引用,示例如下:

1class MyClassFive{ 2 var cls:MyClassSix 3 init(param:MyClassSix){ 4 cls = param 5 } 6 deinit{ 7 print("ClassFive deinit") 8 } 9} 10class MyClassSix{ 11 var cls:MyClassFive? 12 deinit{ 13 print("ClassSix deinit") 14 } 15} 16var obj6:MyClassSix? = MyClassSix() 17var obj5:MyClassFive? = MyClassFive(param: obj6!) 18obj6?.cls = obj5 19obj5=nil 20obj6=nil 21//没有打印任何信息

上面的示例也会造成循环引用,然而MyClassFive类中的cls属性为常量不可为nil,不可使用weak弱引用来做Swift中又提供了一个关键字unowned无主引用来处理这样的问题,示例如下:

1class MyClassFive{ 2 unowned var cls:MyClassSix 3 init(param:MyClassSix){ 4 cls = param 5 } 6 deinit{ 7 print("ClassFive deinit") 8 } 9} 10class MyClassSix{ 11 var cls:MyClassFive? 12 deinit{ 13 print("ClassSix deinit") 14 } 15} 16var obj6:MyClassSix? = MyClassSix() 17var obj5:MyClassFive? = MyClassFive(param: obj6!) 18obj6?.cls = obj5 19obj5=nil 20obj6=nil

关于弱引用和无主引用,其区别主要是在于:

1.弱引用用于解决Optional值的引起的循环引用。

2.无主引用用于解决非Optional值引起的循环引用。

3.个人以为,弱引用可用下图表示:

4.无主引用可用如下图表示:

若将上面的代码修改如下,程序会直接崩溃:

1class MyClassFive{ 2 unowned var cls:MyClassSix 3 init(param:MyClassSix){ 4 cls = param 5 } 6 deinit{ 7 print("ClassFive deinit") 8 } 9} 10class MyClassSix{ 11 var cls:MyClassFive? 12 deinit{ 13 print("ClassSix deinit") 14 } 15} 16var obj6:MyClassSix? = MyClassSix() 17var obj5:MyClassFive? = MyClassFive(param: obj6!) 18obj6?.cls = obj5 19obj6=nil 20obj5?.cls

上面所举的例子满足了两种情况,一种是两类实例引用的属性都是Optional值的时候使用weak来解决循环引用,一种是两类实例有一个为非Optional值的时候使用unowned来解决循环引用,然而还有第三种情况,两类实例引用的属性都为非Optional值的时候,可以使用无主引用与隐式拆包结合的方式来解决,这也是无主引用最大的应用之处,示例如下:

1class MyClassSeven{ 2 unowned var cls:MyClassEight 3 init(param:MyClassEight){ 4 cls = param 5 } 6 deinit{ 7 print("ClassSeven deinit") 8 } 9} 10class MyClassEight{ 11 var cls:MyClassSeven! 12 init(){ 13 cls = MyClassSeven(param:self) 14 } 15 deinit{ 16 print("ClassEight deinit") 17 } 18} 19var obj7:MyClassEight? = MyClassEight() 20obj7=nil

除了在两个类实例间会产生循环引用,在闭包中,也可能出现循环引用,当某个类中包含一个闭包属性,同时这个闭包属性中又使用了类实例,则会产生循环引用,示例如下:

1class MyClassNine { 2 var name:String = "HS" 3 lazy var closure:()->Void = { 4 //闭包中使用引用值会使引用+1 5 print(self.name) 6 } 7 deinit{ 8 print("ClassNine deinit") 9 } 10} 11var obj9:MyClassNine? = MyClassNine() 12obj9?.closure() 13obj9=nil 14//不会打印析构信息

Swift中提供了闭包的捕获列表来对引用类型进行弱引用或者无主引用的转换:

1class MyClassNine { 2 var name:String = "HS" 3 lazy var closure:()->Void = { 4 [unowned self]()->Void in 5 print(self.name) 6 } 7 deinit{ 8 print("ClassNine deinit") 9 } 10} 11var obj9:MyClassNine? = MyClassNine() 12obj9?.closure() 13obj9=nil

捕获列表以中括号标识,多个捕获参数则使用逗号分隔。

专注技术,热爱生活,交流技术,也做朋友。

——珲少 QQ群:203317592

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )