iOS ARC中引用计数的实现
iOS weak 的实现
ARC中的数据结构以及寻址方式
一、 alloc
-
alloc
---->_objc_rootAlloc
---->callAlloc
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
if (slowpath(checkNil && !cls)) return nil;
#if __OBJC2__
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
// No alloc/allocWithZone implementation. Go straight to the allocator.
// fixme store hasCustomAWZ in the non-meta class and
// add it to canAllocFast's summary
if (fastpath(cls->canAllocFast())) {
// No ctors, raw isa, etc. Go straight to the metal.
bool dtor = cls->hasCxxDtor();
id obj = (id)calloc(1, cls->bits.fastInstanceSize());
if (slowpath(!obj)) return callBadAllocHandler(cls);
obj->initInstanceIsa(cls, dtor);
return obj;
}
else {
// Has ctor or raw isa or something. Use the slower path.
/*1*/ id obj = class_createInstance(cls, 0);
if (slowpath(!obj)) return callBadAllocHandler(cls);
/*2*/ return obj;
}
}
#endif
// No shortcuts available.
if (allocWithZone) return [cls allocWithZone:nil];
return [cls alloc];
}
// 实际对象创建函数
static __attribute__((always_inline))
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
if (!cls) return nil;
assert(cls->isRealized());
// Read class's info bits all at once for performance
bool hasCxxCtor = cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
size_t size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (!zone && fast) {
/*1*/ obj = (id)calloc(1, size);
/*2*/ if (!obj) return nil;
/*3*/ obj->initInstanceIsa(cls, hasCxxDtor);
}
else {
if (zone) {
obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
if (!obj) return nil;
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls);
}
if (cxxConstruct && hasCxxCtor) {
obj = _objc_constructOrFree(obj, cls);
}
return obj;
}
-
if (fastpath(!cls->ISA()->hasCustomAWZ()))
判断有没有自定义的alloc方法,默认没有所以if条件为真,canAllocFast
方法固定返回false,所以会执行class_createInstance
方法 -
class_createInstance
---->_class_createInstanceFromZone
-
_class_createInstanceFromZone
中调用calloc
分配空间 -
initInstanceIsa
--->initIsa
为对象结构体中的 isa 赋值 -
alloc
结束 总得来说就是 分配空间、为isa赋值。init
默认返回自身 没有额外操作 -
alloc
不会使引用计数+1 此时isa中的引用计数为0,retainCount
方法在返回值上+1,所以创建完对象引用计数就会变成1。
...
// isa 赋值
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3;
二、 retain
-
retain
---->rootRetain
----->rootRetain(bool tryRetain, bool handleOverflow)
// 由retain调用至此的两个参数 为false 只保留部分关键代码
ALWAYS_INLINE id
objc_object::rootRetain(bool tryRetain, bool handleOverflow)
{
// TaggedPointer 是苹果针对64架构的优化,不使用引用计数,所以直接返回本身
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;
...
uintptr_t carry;
/*1*/ newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++
if (slowpath(carry)) {
// newisa.extra_rc++ overflowed
// 第一次调用此法时handleOverflow为false,所以溢出时 调用 rootRetain_overflow 方法内部重新调用 rootRetain,此时handleOverflow为true
if (!handleOverflow) {
ClearExclusive(&isa.bits);
return rootRetain_overflow(tryRetain);
}
// Leave half of the retain counts inline and
// prepare to copy the other half to the side table.
if (!tryRetain && !sideTableLocked) sidetable_lock();
/*2*/ sideTableLocked = true;
/*3*/ transcribeToSideTable = true;
/*4*/ newisa.extra_rc = RC_HALF;
/*5*/ newisa.has_sidetable_rc = true;
}
} while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));
if (slowpath(transcribeToSideTable)) {
// Copy the other half of the retain counts to the side table.
/*6*/ sidetable_addExtraRC_nolock(RC_HALF);
}
if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
return (id)this;
}
-
addc(newisa.bits, RC_ONE, 0, &carry)
对 isa中保留的引用计数extra_rc
+1 - 如果在上一步操作中 引用计数溢出(64位操作系统中,引用计数占8位),则将
extra_rc
设置为RC_HALF extra_rc 总共为 8 位,RC_HALF = 0b10000000 代表一半的引用计数
,设置has_sidetable_rc
为true(代表有额外的引用计数保存在SiteTables里面), 然后调用sidetable_addExtraRC_nolock(RC_HALF)
方法,在SiteTables
中保存一条记录,将多余的一半引用计数保存进去 - 总结:此方法分为两种情况
未溢出:则在extra_rc
的基础上进行+1
溢出:总大小为8位,所以将一半的引用计数RC_HALF
,保存到extra_rc
中,一半的引用计数保存到SideTables
-
SideTables
保存对象额外的引用计数和弱引用,后面在单独分析
二、 release
-
release
---->rootRelease
----->rootRelease(bool performDealloc, bool handleUnderflow)
正常情况的 release:
// rootRelease(true, false) performDealloc = true handleUnderflow = false
bool objc_object::rootRelease(bool performDealloc, bool handleUnderflow) {
isa_t oldisa;
isa_t newisa;
do {
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
uintptr_t carry;
newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry);
} while (!StoreReleaseExclusive(&isa.bits, oldisa.bits, newisa.bits));
return false;
}
-
LoadExclusive
获取 isa -
subc
对 extra_rc -1 -
StoreReleaseExclusive
保存最新的 isa.bits
发生 underflow 的 release
bool objc_object::rootRelease(bool performDealloc, bool handleUnderflow) {
isa_t oldisa;
isa_t newisa;
do {
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
uintptr_t carry;
newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry);
if (carry) goto underflow;
} while (!StoreReleaseExclusive(&isa.bits, oldisa.bits, newisa.bits));
...
underflow:
newisa = oldisa;
if (newisa.has_sidetable_rc) {
if (!handleUnderflow) {
return rootRelease_underflow(performDealloc);
}
size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);
if (borrowed > 0) {
newisa.extra_rc = borrowed - 1;
bool stored = StoreExclusive(&isa.bits, oldisa.bits, newisa.bits);
return false;
}
}
}
- 在正常 release 的基础下,如果发生underflow,则调用
goto underflow
- 判断
newisa.has_sidetable_rc
是否保存有引用计数在 SideTables - 从SideTables中取出 RC_HALF个引用计数在 -1 之后赋值给isa.bits
- 后续根据stored判断是否赋值成功,如果没有成功在进行补救操作或者回归操作
release 调用dealloc
bool objc_object::rootRelease(bool performDealloc, bool handleUnderflow) {
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);
if (carry) goto underflow;
} while (!StoreReleaseExclusive(&isa.bits, oldisa.bits, newisa.bits));
...
underflow:
newisa = oldisa;
if (newisa.deallocating) {
return overrelease_error();
}
newisa.deallocating = true;
StoreExclusive(&isa.bits, oldisa.bits, newisa.bits);
if (performDealloc) {
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return true;
}
- 如果没有SideTables存储引用计数,并且发生underflow,则说明对象已经没有引用,需要被释放
- 直接调用 objc_msgSend 向当前对象发送 dealloc 消息。
- 利用isa中的
deallocating
标志位,保证dealloc只会被调用一次
二、 retainCount
inline uintptr_t
objc_object::rootRetainCount()
{
if (isTaggedPointer()) return (uintptr_t)this;
sidetable_lock();
isa_t bits = LoadExclusive(&isa.bits);
ClearExclusive(&isa.bits);
if (bits.nonpointer) {
uintptr_t rc = 1 + bits.extra_rc;
if (bits.has_sidetable_rc) {
rc += sidetable_getExtraRC_nolock();
}
sidetable_unlock();
return rc;
}
sidetable_unlock();
return sidetable_retainCount();
}
retainCount = 1 + bits.extra_rc + sidetable_getExtraRC_nolock
- 引用计数 = 1 + isa中存储的数量 + SideTables中存储的引用计数
二、 Dealloc
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
}
else {
object_dispose((id)this);
}
}
当引用计数为0时,系统会自动调用dealloc方法来释放资源
- 如果对象没有弱引用、没有关联对象、没有c++析构函数、没有额外的引用计数存在在SideTables中,则直接调用free函数释放空间
- 否则调用
object_dispose
方法
static id
_object_dispose(id anObject)
{
if (anObject==nil) return nil;
objc_destructInstance(anObject);
anObject->initIsa(_objc_getFreedObjectClass ());
free(anObject);
return nil;
}
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
- 调用objc_destructInstance, 调用c++析构函数,移除关联对象,移除SideTables表中相关联的所有数据
- 将isa存储的类指针设为nil
- 调用free函数释放空间
补充
在MRC时代,经常写下面的代码:
- (void)dealloc {
self.array = nil;
self.string = nil;
// ... //
// 非Objc对象内存的释放,如CFRelease(...)
// ... //
[super dealloc];
}
但是ARC中不需要显示释放属性,只需要移除观察者、Timer等,并且不需要调用[super dealloc]
这是因为系统为我们自己实现了这些方法,而这个方法就是.cxx_destruct
,这个方法本来是用来调用C++对象析构函数的,而在ARC中,编译器自动为我们生成这个方法用来释放属性。
在下面链接中有.cxx_destruct
的具体分析
http://blog.sunnyxx.com/2014/04/02/objc_dig_arc_dealloc/
总得来说,对象从创建到销毁总共经历以下几个阶段。
1️⃣ alloc,分配空间,并且初始化isa,init返回自身,此时retainCount默认为1
2️⃣ retain, 增加对象引用计数,增加isa中的bits.extra_rc,如果溢出,则保存到SideTables中
3️⃣ release,减少对象引用计数,减少isa中的bits.extra_rc,如果溢出,则取出SideTables中的引用计数,如果SideTables中没有存储,则对象释放,调用 dealloc方法
4️⃣ dealloc,释放对象,移除对象存储的相关信息,包括关联对象、引用计数、弱引用表。