swift的ARC
在swift中,一个对象在概念上有三种refcount。strong和unowned存在inlineref字段中,weak存在sidetable的refcount中。
- Strong RC : 记录这个对象的强引用。当强引用为0时,这个对象被deinited。此时读取unowned reference会出错,读取weak reference会得到nil。Strong RC是通过额外引用计数extra记录的,也就是说,extra为0,代表实际的引用计数为1。
- Unowned RC: 记录对象的无主引用。无主引用初始化为1。在deinite完成后-1,当无主引用为0时,这个对象分配的内存会被free掉。
- weak RC: 记录这个对象的弱引用。弱引初始化也为1,在分配的内存free掉后-1。当弱引用为0,这个对象的side table entry会被释放掉。
对象初始化时是没有side table的。在下面情况下会生成:
- 有弱引用
- strong RC或者unowned RC计数溢出了
- 有associated object
- 等
强引用和无主引用指向这个对象。
弱引用指向这个对象的side table(声明一个weak变量相当于定义了一个WeakRefrence对象,这个对象会持有一个WeakReferenceBits, WeakReferenceBits是通过sidetableentry初始化的,sidetableentry包含了引用对象的指针和引用计数,所以当strongRC为0时,对象可以通过HeapObject的ref找到sidetableentry,并把其中的对象指针设置为nil,这样弱引用访问拿到的都是nil)。
swift中每个对象都是一个HeapObject。下面是他的定义:
|
|
|
|
|
|
对象的生命周期状态机
Live
(没有side table):对象的refcounts被初始化为:strong 1, unowned 1 ,weak 1。
Strong varibale正常工作。
unowned variable正常工作。
没有weak variable。
如果你声明了一个weak variable,那么会添加side table,对象变成Live with side table状态。
当strong RC变为0了,会调用deinit(),然后对象状态变为
DEINITING
。Live
(有side table)weak variable正常工作。
其他的和Live相同。
DEINITING
(没有side table)deinit()正在进行。
强引用变量将不能使用。
使用unowned variable会引发swift_abortRetainUnowned() 错误。
unowned variable的赋值正常工作。
weak variable 不能使用。(因为没有side table就没有弱引用,所以也不存在使用弱引用变量)。
weak variable的赋值会存为nil。
当deinit()完成后,会调用swift_deallocObject 。swift_deallocObject 会调用canBeFreedNow() 来检查还有没有weak或者unowned references。如果canBeFreedNow返回yes,那么这个对象被free掉,对象变为
DEAD
,否则,将减少无主引用,对象变为DEINITED
状态。DEINITING
有side tableweak varibale为nil。canBeFreedNow 会一直返回false,所以这种情况不可能直接变为DEAD。
其他同上。
DEINITED
没有side table。deinit()完成了,不过无主引用unowned references还存在。
Strong variable的操作(store和load)不可能发生。
Unowned varibale的store不可能发生。
Unowned varibale的load会报swift_abortRetainUnowned() 。
weak variable的操作不能执行。
如果unowned RC为0了,对象会被free掉,然后变为
DEAD
DEINITED
有side table。weak variable的load会返回nil。
Weak variable的store不可能发生。
当unowned RC为0时,对象被free掉。减少weak RC, 对象变为FREED。
其他的同上。
FREED
没有side table不可能发生,因为FREED需要weak RC, 有weak RC就一定有side table。
FREED
有side table。对象虽然free了,但是指向side table的weak references还存在。
strong variable的操作不可能发生。
unowned variable的操作不可能发生。
weak variable的load会返回nil。
Weak variable的store不可能发生。
当weak RC为0时,side table entry会free掉,对象变为
DEAD
DEAD
对象和他的side table都释放了。
循环强引用
循环强引用,导致了strongRC永远都不会为0,所以对象永远也不会deinit,也就不会free,sidetable有的话也不会释放掉。
我们可以使用weak或者unowned来打破,那什么时候使用呢?其实根据weak和unowned的特性,我们就能知道:
- weak会在对象deinit后将sidetable中的对象指针置为nil,所以我们用weak引用不会出现访问野指针的情况,不过缺点也很明显,用了weak,引用计数就需要通过sidetable来记录,分配了更多的空间(sidetableentry),调用的层级也深一点(ref的操作最终都交给了sidetableentry)。
- unowned在对象deinit后,访问对象将会崩溃,不过好处是无主引用记录在inlineref中,不会增加额外的东西(引用计数溢出了例外哈)
ok,结论就是如果你非常确定引用的对象在使用期间一直存在的,那么就用unowned。
如果你不太确定或者对象就是可能在使用期间已经被释放掉了,那么使用weak。