引用计数:顾名思义,就是对象当前被引用的计数retainCount。当retainCount为0时,表面当前对象没有被任何其它对象引用;当retainCount不为0时,则对象任被系统中其它对象所引用,所以此时对象还仍被使用中,内存不能被系统所回收。只用当retainCount为0时,对象才能被系统所回收。
1、自己生成的对象,自己持有。
当我们使用alloc,new,copy,mutableCopy生成对象时,则当前对象自己持有该引用计数。
NSObject* object = [NSObject new];
2、非自己生成的对象,自己也能持有。
当我们使用非方法一的方式生成的对象时,此时可能通过其它方法创建生成的对象当前对象并不持有,此时若是想对象自己持有对象,则我们可以使用以下两种方式:
// 方法一
NSMutableArray* array = [NSMutableArray array];
[array retain];
//方法二
NSMutableArray* _strong array = [NSMutableArray array];
3、不再需要自己持有的对象时释放对象。
这个点既是当对象不被任何其它对象引用时,引用计数retainCount为0时则对对象进行释放。
4、非自己持有的对象无法释放。
对象若是不持有该对象,则不能对对象执行release操作,否则对象运行时将会报错。
1、retain操作
objc_object::rootRetain(bool tryRetain, bool handleOverflow)
{
if (isTaggedPointer()) return (id)this;
bool sideTableLocked = false;
bool transcribeToSideTable = false;
isa_t oldisa;
isa_t newisa;
do {
transcribeToSideTable = false;
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
else return sidetable_retain();
}
uintptr_t carry;
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++
if (slowpath(carry)) {
if (!tryRetain && !sideTableLocked) sidetable_lock();
sideTableLocked = true;
transcribeToSideTable = true;
newisa.extra_rc = RC_HALF;
newisa.has_sidetable_rc = true;
}
} while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));
if (slowpath(transcribeToSideTable)) {
sidetable_addExtraRC_nolock(RC_HALF);
}
return (id)this;
}
原理分析:
1、当引用计数不溢出时,则将当前的引用计数extra_rc变量值加1;
2、当引用计数溢出时,也即是当extra_rc变量大于255时,此时将RC_HALF的值也即是128存入sideTable表中,当前引用计数extra_rc的值(256-128=128)为128,之后若是有对象进行引用则再从128开始执行加1计算;在这里代码中有个注意点,当把RC_HALF(128)存进去sideTable表时,delta_rc因为左移两位所以值是512,只是因为sideTable表中低两位是用于作为标识位使用,所以是要左移两位为512,其实存放的还是溢出对象一半的值即128!
2、release操作
objc_object::rootRelease(bool performDealloc, bool handleUnderflow)
{
if (isTaggedPointer()) return false;
bool sideTableLocked = false;
isa_t oldisa;
isa_t newisa;
retry:
do {
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
uintptr_t carry;
newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry); // extra_rc--
if (slowpath(carry)) {
goto underflow;
}
} while (slowpath(!StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits)));
return false;
underflow:
newisa = oldisa;
size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);
if (borrowed > 0) {
newisa.extra_rc = borrowed - 1; // redo the original decrement too
bool stored = StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits);
if (!stored) {
isa_t oldisa2 = LoadExclusive(&isa.bits);
isa_t newisa2 = oldisa2;
if (newisa2.nonpointer) {
uintptr_t overflow;
newisa2.bits =
addc(newisa2.bits, RC_ONE * (borrowed-1), 0, &overflow);
if (!overflow) {
stored = StoreReleaseExclusive(&isa.bits, oldisa2.bits,
newisa2.bits);
}
}
}
}
newisa.deallocating = true;
__sync_synchronize();
if (performDealloc) {
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return true;
}
原理分析:
1、当对象之前没存在溢出时,则直接将对象引用计数extra_rc值减1;
2、当对象之前存在溢出时,则sideTable表中则存放着对象之前溢出的值,则将RC_HALF传入取出128的值再进行减1操作,而且当在sideTable表中取了128的值后,当然对应sideTable表中的值也要进行减去RC_HALF的值也即是128的操作;再将对象计数值进行保存,若保存过程对象出现问题,则将对象直接销毁;
3、若对象已经处于正在释放时,则直接调用方法进行释放;
3、retainCount操作
inline uintptr_t
objc_object::rootRetainCount()
{
if (isTaggedPointer()) return (uintptr_t)this;
if (bits.nonpointer) {
uintptr_t rc = 1 + bits.extra_rc;
if (bits.has_sidetable_rc) {
rc += sidetable_getExtraRC_nolock();
}
return rc;
}
return sidetable_retainCount();
}
原理分析:
获取对象引用计数retainCount的操作,则是将对象的bits.extra_rc值和在sideTable表中存放的值进行相加得到的,但为什么还要加1呢,是因为bits.extra_rc存放的引用计数减1后的值,也即是当retainCount等于6时,则extra_rc则为5,这就是要进行加1操作的原因。
以上则是对对象引用计数原理的介绍,生成对象遵循引用计数规则对对象进行合理有效地内存管理。