objc原始碼解讀-物件生命週期

Yang1492955186752發表於2017-12-13

在Objective-C中,當我們建立一個物件,並呼叫對應的方法的時候這個物件的生命週期是如何的,方法是如何呼叫的,我們應該有一個最基本的認識, 假定我們有一個自定義類,CustomClass,繼承自NSObject:

@interface CustomClass : NSObject

- (void)doSomething;
複製程式碼

當我們呼叫

CustomClass *customObj = [[CustomClass alloc] init];
[customObj doSomething];
複製程式碼

runtime到底在背後做了什麼?是如何管理引用計數的?又是如何呼叫呼叫方法的。這就需要我們對objc原始碼進行刨根問底了。

首先我們看看,CustomClass傳送alloc訊息,

//1.首先呼叫_objc_rootAlloc,引數為CustomClass
+ (id)alloc {
return _objc_rootAlloc(self);
}

//2.再呼叫callAlloc
cls:CustomClass
checkNil:是否檢查Cls
allocWithZone:是否分配到指定空間,預設為false,內部會對其進行優化
id _objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
複製程式碼
//3.callAlloc內部實現
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
//fastpath和slowpath是編譯器對判斷條件做預知的優化
比如checkNil && !cls 這個條件極大多數情況都為false
這時候提前呼叫slowpath提高程式碼的效率,反之則會呼叫fastpath
if (slowpath(checkNil && !cls)) return nil;

//在__OBJC2__中,
#if __OBJC2__
//多數情況下Class內部都不會重寫allocWithZone所以這裡會使用fastpath
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.
// 假如能快速分配,並且有c++解構函式
bool dtor = cls->hasCxxDtor();
id obj = (id)calloc(1, cls->bits.fastInstanceSize());
if (slowpath(!obj)) return callBadAllocHandler(cls);
// 建立物件,初始化isa指標
obj->initInstanceIsa(cls, dtor);
return obj;
}
else {
// Has ctor or raw isa or something. Use the slower path.
//假如沒有c++解構函式,傳入class和extraBytes:0
id obj = class_createInstance(cls, 0);
if (slowpath(!obj)) return callBadAllocHandler(cls);
return obj;
}
//無論是否能快速建立,都會呼叫initIsa進行isa指標的初始化
}
#endif

// No shortcuts available.
if (allocWithZone) return [cls allocWithZone:nil];
return [cls alloc];
}
複製程式碼

生成物件customObj後呼叫init

//最終會呼叫這個函式,直接返回obj
id
_objc_rootInit(id obj)
{
// In practice, it will be hard to rely on this function.
// Many classes do not properly chain -init calls.
return obj;
}
複製程式碼

Retain:

//最終都會呼叫這個方法,首先判斷是否是nonpointer,假如是,就不會呼叫retain
//nonpointer指標的值不再是地址了,而是真正的值,並且因為沒有isa,因此不是真正的物件
id
objc_object::sidetable_retain()
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
從SideTables中獲得對對應table的引用避免開闢新的記憶體空間
SideTable& table = SideTables()[this];

table.lock();
//獲取table中對應的引用計數避免開闢新的記憶體空間
size_t& refcntStorage = table.refcnts[this];
//假如引用計數沒有溢位
if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
//引用計算refcntStorage左移一位
refcntStorage += SIDE_TABLE_RC_ONE;
}
table.unlock();
return (id)this;
}
複製程式碼

Release

uintptr_t
objc_object::sidetable_release(bool performDealloc)
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
SideTable& table = SideTables()[this];

bool do_dealloc = false;

table.lock();
RefcountMap::iterator it = table.refcnts.find(this);
//沒有找到引出計數
if (it == table.refcnts.end()) {
do_dealloc = true;
table.refcnts[this] = SIDE_TABLE_DEALLOCATING;
} else if (it->second < SIDE_TABLE_DEALLOCATING) {
//可能設定了弱引用
// SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it.
do_dealloc = true;
it->second |= SIDE_TABLE_DEALLOCATING;
} else if (! (it->second & SIDE_TABLE_RC_PINNED)) {
//假如取得的value沒有溢位,右移一位
it->second -= SIDE_TABLE_RC_ONE;
}
table.unlock();
if (do_dealloc  &&  performDealloc) {
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return do_dealloc;
}
複製程式碼

相關文章