Objective-C 程序里的对象一起组成一张对象图:由各个对象和其他对象的关系(或引用)而形成的网络。对象之间的引用可分为一对一,还有一对多也就是通过对象集合引用。对于对象图非常重要,因为它是使对象保持生命力的一个重要因素。编译器会检查对象图中所用到的强与弱,根据需求保持对象发出的,或者是释放对象信息。
在 C 语言或 Objective-C 语言中,可以使用含有全局变量、实例变量或本地变量的结构来构造对象间的引用。这些结构各自都有自己暗含的作用域。比如,本地变量引用的一个对象的作用域就是声明它的函数块所在的位置。和它一样重要的是,对象之间的引用也存在着强与弱。
强引用: 会指示出自己的所有者是谁,指向别人的对象拥有被指向的对象。
弱引用则是指向别人的对象和被指向的对象之间没有从属关系。对象的生命周期由它的强引用数量多少决定。只要对象有强引用关系,就不会被释放。
Objective-C 里的引用默认都是强引用。通常来说这很方便,让编译器管理对象的运行时生命周期,当你使用对象时它们不会被释放。但是如果粗心未作全面检查,对象间的强引用可能会形成无限循环,如下图左边所示。这样的循环链在运行时会导致运行时不会释放任何一个对象,它们都有指向自己的强引用。继而,这样的死循环就造成了内存泄露。
从上面图的对象能够看出,如果你取消 A 和 B 之间的引用,则 B、C、D、E 构成的子对象图将永不会从内存中释放,因为这些对象每一个都有强引用,形成了一个死循环。如果在 E 和 B 之间引入弱引用,就可以打破强引用死循环了。
为了修正强引用死循环的问题,精明的程序员会使用弱引用。运行时会持续跟踪对象的弱引用。一旦对象不再有强引用,运行时就会从释放该对象,并将所有指向该对象的引用改为 nil。对变量来说(全局、实例和本地变量),在对象名前面加上 __weak 限定词就可以将其标记为弱引用。对于属性来说,可以使用 weak 选项。
在以下这几类引用中,你应该使用弱引用:
1. 委托
在《设计模式》篇里,“用设计模式让应用开发流水线化”教程将向你详解委托和目标机制。
2.未被顶级对象引用的插座变量(Outlet)
插座变量是对象间的一种连接(或引用),被归档在故事版文件或 nib 文件中,当应用运行并载入故事版或 nib 文件时就会恢复插座变量。故事版或 nib 文件中顶级对象的插座变量一般而言是窗口、视图、视图控制器或其他控制器等,应该为 强引用(默认的,或未标记的)。
3.目标
4.块对象中指向 self 的引用
块对象会对它捕获的变量产生强引用。如果在块对象里运用了self的话,那么就会对self产生引用。所以,如果 self 对块对象也有强引用,一般情况下都是这样的,就形成了强引用死循环。为了避免死循环,你需要在块对象的外面创建一个指向 self 的 弱(或 __block)引用。