Swift的自动引用计数
Posted mazegong
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swift的自动引用计数相关的知识,希望对你有一定的参考价值。
前言
在ios5之后apple推出了相对于MRC(Mannul Reference Counting)
的ARC(Automatic Reference Counting)
的内存管理机制,前者是对内存的手动管理,后者是系统对内存的智能化管理。其实我学习iOS的时候已经推出到iOS7了,对于MRC
机制我甚至连一知半解都算不上。有时候和iOS老司机交流的时候他们对于各种内存都很熟悉,感觉包括我在内的很多最近两年刚学习iOS开发的同学,都属于填鸭式的学习吧,知其然不知其所以然,甚至都没耐心去读优秀的源码。记得有次去面试,别人问我对于AFNetworing
和SDWebImage
的源码了解有多深,其实我是懵逼的。同时,也正是这种茫然,促使我要系统的读一遍Swift的系统教程,由于英语确实有点渣,并且6月份Swift3.0就要出来了,所以才决定看极客学院翻译的这本教材。并且一段时间以来,写份读书笔记和仅仅是浏览一遍教材,感受和记忆是完全不一样的。我想,读一遍之后,再回头看看自己的笔记,改改错别字,理理不通顺的地方,也许可能效果更好些吧。
- 自动引用计数的工作机制
- 自动引用计数实践
- 类实例之间的循环强引用
- 解决实例之间的循环强引用
- 闭包引起的循环强引用
- 解决闭包引起的循环强引用
Swift使用自动引用计数机制来跟踪和管理应用程序的内存。通常情况下,Swift内存管理机制会一直起作用,我们无需自己来考虑内存的管理。ARC
会在类的实例不在被使用时,自动释放其占用的内存。
然而在少数情况下,ARC
为了能帮忙我们管理内存,需要更多的关于代码之间关系的信息。所以了解如何启用ARC
来管理应用程序的内存还是很有必要的。
需要注意的是,引用计数仅仅应用于类的实例。结构体和枚举都是值类型,不是引用类型,也不是通过引用的方式存储和传递。
分条详述
-
自动引用计数的工作机制
当创建一个类的新的实例时,
ARC
会分配一大块内存来存储实例的信息。内存中会包含实例的类型信息,以及这个实例的所有属性的相关值。此外,当实例不再被使用时,
ARC
释放实例所占的内存,并让释放的内存能挪作他用。这确保了不再被使用的实例,不会一直占用内存空间。然而当
ARC
收回和释放了正在被使用的实例,该实例的属性和方法将不能再被访问和调用。实际上,当我们视图访问一个被释放掉的类实例时,应用程序很可能会崩溃。为了确保使用中的实例不会被销毁,
ARC
会跟踪和计算每一个实例正在被多少属性,常量和变量所引用。哪怕实例的引用数为1,ARC
都不会销毁这个实例。为了使上述成为可能,无论我们将实例赋值给属性、常量或变量,他们都会创建此实例的强引用。之所称为 强引用,因为他们都将实例牢牢的保持住,只要强引用还在,实例是不允许被销毁的。
-
自动引用计数实践
这里通过一个例子介绍自动引用计数在代码中的调用情况,代码的具体作用写在注释里:
<code class="hljs php has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Person</span> {</span> let name: String <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 姓名</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 构造器,设置名字</span> init(personName name: String) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span>.name = name <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">print</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\\(name) is being initialized"</span>) } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 析构器,实例被注销的时候调用</span> deinit { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">print</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\\(name) is being deinitialized"</span>) } } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 创建实例,这里作说明:因为是Person类的可选类型,所以执行下面代码的时候,Person并没有被创建,此时,Person类的引用数为0</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">var</span> reference1: Person? <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">var</span> reference2: Person? <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">var</span> reference3: Person? <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 下面代码Person才真正的有了内存地址</span> reference1 = Person(personName: <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Jack"</span>) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 此时第一次输出:Jack is being initialized</span> reference2 = reference1 <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Person类有两个常量引用</span> reference3 = reference1 <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 三个</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 下面另1、2为空</span> reference1 = nil reference2 = nil <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 此时,Person类的析构函数不执行,因为reference3还在引用它</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 令reference3 = nil,此时Person类会被释放</span> reference3 = nil <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 此时,输出: Jack is being reinitialized</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li></ul>
-
类实例之间的循环强引用
在上面的例子中,
ARC
会跟踪所创建的Person
实例的引用计量,并且会在Person
实例不再需要时销毁它。然而,我们可能会写出一个类的强引用数永远不能变为0的代码。如果两个类实例互相持有对方的强引用,因而每个实例都让对方一直存在,这种情况就是所谓循环强引用。可以通过定义类之间的关系为弱引用或无主引用,以替代强引用,从而解决循环强引用的问题。了解解决方案之前,先看一下强循环引用在代码中是怎样产生的吧。创建一个人类
Person
类类型和一个公寓Apartment
类类型,人可能住在公寓,也可能不在,公寓可能有人,也可能没人,所以它们在代码中都是可选类型的:<code class="hljs javascript has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 住户类</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> Person { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">let</span> name: <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">String</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 姓名</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">var</span> apartment: Apartment? <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 可选,如果用 let 修饰或者去掉 ?,那么必须赋初始值</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 构造器,设置用户的名字</span> init(personName name: <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">String</span>) { self.name = name } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 析构器,Person类被销毁的时候调用</span> deinit { print(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\\(name) is being deinitalized"</span>) } } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 公寓类</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> Apartment { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">let</span> unit: <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">String</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 公寓单元</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">var</span> person: Person? <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 住户</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 构造器,获取单元名</span> init(apartmentUnit unit: <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">String</span>) { self.unit = unit } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 析构器</span> deinit { print(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\\(unit) is being deinitalized"</span>) } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li></ul>
创建实例后,出现下图所示的强引用关系:
<code class="hljs cs has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 创建实例,此时应景产生强引用的关系</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">var</span> john = Person(personName: <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"John"</span>) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">var</span> unit4A = Apartment(apartmentUnit: <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"unit4A"</span>)</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>
下面两行代码就会产生循环强引用:
<code class="hljs avrasm has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">// 下面的相互持有,将会产生循环强引用 john<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.apartment</span> = unit4A unit4A<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.person</span> = john</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>
此时,即使令
john
、unit4A
都设置为nil
,没有任何一个析构函数会被调用,。循环强引用会一直阻止john
和unit4A
类实例的销毁,这就在引用程序中造成了内存泄漏。<code class="hljs lua has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">// 循环强引用不会被断开,并且下面的写法会产生编译错误:Nil cannot be assigned to <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">type</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'Person'</span>、<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'Aparement'</span> john = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">nil</span> unit4A = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">nil</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>
-
解决实例之间的循环强引用
Swift提供了两种办法来解决你在使用类的属性时所遇到的循环强引用问题:弱引用
weak reference
和无主引用unowned reference
。弱引用和无主引用允许循环引用中的一个实例引用另外一个实例而不保持强引用。这样实例能够互相引用而不产生循环强引用。
对于生命周期中会变为
nil
的实例使用弱引用。相反的,对于初始化赋值后再也不会被赋值为nil
的实例,使用无主引用。-
弱引用
弱引用不会对其引用的实例保持强引用,因为不会阻止ARC
销毁被引用的这个实例,这个特性阻止了引用变为循环强引用。声明变量或属性时,前面加上weak
关键字表明这是一个弱引用。弱引用必须声明为变量,表明其值能在运行时被修改。弱引用不能被声明为常量。因为弱引用可以没有值,必须将每一个弱引用声明为可选类型。在Swift中,推荐使用可选类型描述可能没有值的类型。
这里只写一个简单地示例代码,即可看到效果,在实际写代码的时候,发现一些自己忽略的问题,写在了注释里:
<code class="hljs php has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 住户</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Person</span> {</span> let name = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Jack"</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">var</span> apartment: Apartment? deinit { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">print</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"住户信息被销毁"</span>) } } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 公寓</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Apartment</span> {</span> let unit = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"4A"</span> weak <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">var</span> person: Person? deinit { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">print</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"公寓信息被销毁"</span>) } } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 注意点1:Person、Apartment必须是可选类型,当Person不是可选类型的时候,最后不能销毁循环强引用,即:personOne = nil 、 apartmentOne = nil 这两行代码会抛出异常</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">var</span> personOne: Person? = Person() <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">var</span> apartmentOne:Apartment? = Apartment() <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 令它们相互持有</span> personOne!.apartment = apartmentOne apartmentOne!.person = personOne <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 注意点2:下面两行代码必须都写,才会执行析构函数,因为它们是相互持有</span> personOne = nil <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 此时什么都不输出</span> apartmentOne = nil <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 输出:住户信息被销毁 公寓信息被销毁</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li></ul>
-
无主引用
和弱引用类似,无主引用不会牢牢保持住引用的实例。和弱引用不同的是,无主引用是永远有值的。因此,无主引用总是被定义为非可选类型non-optional type
,可以在声明属性或变量时,在前面加上关键字unowned
表示这是一个无主引用。由于无主引用是非可选类型,所以不需要在使用它时将它展开(即不可要在后面加感叹号‘!’解包)。无主引用总是可以被直接访问。不过
ARC
无法在实例被销毁后将无主引用设为nil
,因为非可选类型的变量不允许赋值为nil
。使用无主引用,必须确保引用始终指向一个未销毁的实例。下面的例子模拟银行客户
Customer
和信用卡CreditCard
之间的关系。每个用户可能有信用卡,但是每个信用卡必须要有客户,因此信用卡要有一个关于客户的无主引用,以避免循环强引用:<code class="hljs php has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 银行客户</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Customer</span> {</span> let name: String <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">var</span> card: CreditCard? <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 构造器</span> init(customerName name: String) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span>.name = name } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 析构器</span> deinit { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">print</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\\(name) 被销毁"</span>) } } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 信用卡</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">CreditCard</span> {</span> let number: Int <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 无主引用,信用卡的用户信息必不为空</span> unowned let customer: Customer <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 构造器</span> init(cardNumber: Int, customer: Customer) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span>.number = cardNumber <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span>.customer = customer } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 析构器</span> deinit { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">print</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\\(number) 号信用卡被销毁"</span>) } } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 创建实例,非可选类型不可设置为nil,所以Customer为可选类型</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">var</span> john: Customer? = Customer(customerName: <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"John"</span>) john!.card = CreditCard(cardNumber: <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1234</span>_6789_123, customer: john!)</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li></ul>
此时,客户和信用卡的关系如下图:
如果令
-