引言
我們都知道,Runtime 是 Objective-C 這門動態(tài)語言的核心根穷,只有理解了它姜骡,我們才能夠更好的理解 Objective-C 到底是如何工作的导坟,在編程時,也會更加得心應(yīng)手圈澈。由于時間和精力有限惫周,此次我主要想從以下幾方面來進行 Runtime 源碼的閱讀,日后將會逐步完善士败。由于總體篇幅較長闯两,所以我將會每一部分拆分成一篇文章來具體分析。
目錄
一谅将、對象的本質(zhì)漾狼,了解 isa
二、對象的生命周期
三饥臂、對象的引用計數(shù)
四逊躁、對象的擴展方法
五、Runtime 的應(yīng)用
對象的本質(zhì)隅熙,了解 isa
首先來看一看對象和類的定義
/// Represents an instance of a class.
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
};
// 其內(nèi)部有一個指向 Class 的指針稽煤,而 Class 是什么呢
// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
// 再來看一下 objc_class 的定義,需要注意的是,objc_class 也是繼承自 objc_object囚戚, 由于 objc_object 中已經(jīng)定義了一個 isa 指針酵熙,由于結(jié)構(gòu)體中所有的成員都是 public 的,所以 objc_class 也就擁有了 isa 并且也擁有訪問 isa 的權(quán)限驰坊。
struct objc_class : objc_object {
// Class ISA; // 此時匾二,類中的 isa 指針指向的是 metaClass 元類
Class superclass; // 父類
cache_t cache; // formerly cache pointer and vtable 類的方法緩存,因為 Runtime 時會把第一次遇到的方法緩存到方法緩存中拳芙,此后將直接從緩存中讀取方法察藐,極大提高了效率
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
// class_data_bits_t <==> class_rw_t + 一個 rr/alloc 位
};
所以到這里,我們也就理解到了舟扎,實際上分飞,類也是一個對象
-
我們需要知道的是,在 Objective-C 中睹限,對象的方法并沒有存儲于對象的結(jié)構(gòu)體中(如果每一個對象都保存了自己能執(zhí)行的方法譬猫,那么對內(nèi)存的占用有極大的影響)。在調(diào)用實例方法時邦泄,而是通過 isa 指針來尋找相應(yīng)的類删窒,通過 class_data_bits_t 來尋找類中的方法。具體是如何尋找的顺囊,我們看??
// 首先 class_data_bits_t 中有一個 bits 位 struct class_data_bits_t { // Values are the FAST_ flags above. uintptr_t bits; public: // 這里返回的數(shù)據(jù)是 class_rw_t* 指針類型的數(shù)據(jù),在這個方法中我們可以看出蕉拢,將 bits 與 FAST_DATA_MASK 進行位運算特碳,只取其中的 [3, 47] 位轉(zhuǎn)換成 class_rw_t * 返回诚亚。 class_rw_t* data() { return (class_rw_t *)(bits & FAST_DATA_MASK); } } // data() 返回的是一個 class_rw_t* 指針, class_rw_t 又是什么午乓? // 類中的屬性站宗、方法還有遵循的協(xié)議等信息都保存在 class_rw_t 中: struct class_rw_t { // Be warned that Symbolication knows the layout of this structure. uint32_t flags; uint32_t version; const class_ro_t *ro; method_array_t methods; property_array_t properties; protocol_array_t protocols; Class firstSubclass; Class nextSiblingClass; char *demangledName; #if SUPPORT_INDEXED_ISA uint32_t index; #endif }; // 由此我們可以看出, class_rw_t 中包含了一些關(guān)于類的信息益愈,比如 flag梢灭, 版本號, 方法數(shù)組蒸其, 屬性數(shù)組等敏释。而其中又有一個指向 class_ro_t 的指針, class_ro_t 又是什么摸袁? // 原來钥顽,class_ro_t 中存儲了當前類在編譯期就已經(jīng)確定的屬性、方法以及遵循的協(xié)議靠汁。 struct class_ro_t { uint32_t flags; uint32_t instanceStart; uint32_t instanceSize; #ifdef __LP64__ uint32_t reserved; #endif const uint8_t * ivarLayout; const char * name; method_list_t * baseMethodList; protocol_list_t * baseProtocols; const ivar_list_t * ivars; const uint8_t * weakIvarLayout; property_list_t *baseProperties; method_list_t *baseMethods() const { return baseMethodList; } };
-
所以由此蜂大,我們知道 class_ro_t 保存的是在編譯期時就已經(jīng)確定的方法,所以當在編譯期時蝶怔, class_data_bits_t 將直接指向 class_ro_t 奶浦,而后在 Runtime 時,將會調(diào)用 class_data_bits_t 的 data() 直接將結(jié)果從 class_rw_t 轉(zhuǎn)化成 class_ro_t 指針踢星, 然后再初始化一個 class_rw_t 指針澳叉,此時它中的數(shù)據(jù)都為空,然后再設(shè)置它的 ro 變量和 flag斩狱, 最后再為其設(shè)置正確的 data
/*********************************************************************** * realizeClass * Performs first-time initialization on class cls, * including allocating its read-write data. * Returns the real class structure for the class. * Locking: runtimeLock must be write-locked by the caller **********************************************************************/ static Class realizeClass(Class cls) { runtimeLock.assertWriting(); const class_ro_t *ro; class_rw_t *rw; Class supercls; Class metacls; bool isMeta; if (!cls) return nil; if (cls->isRealized()) return cls; assert(cls == remapClass(cls)); // fixme verify class is not in an un-dlopened part of the shared cache? // 強制轉(zhuǎn)化為 ro ro = (const class_ro_t *)cls->data(); if (ro->flags & RO_FUTURE) { // This was a future class. rw data is already allocated. rw = cls->data(); ro = cls->data()->ro; cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE); } else { // Normal class. Allocate writeable class data. // 這時候首先給 rw 分配內(nèi)存空間并且初始化為 0 rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1); // 使 rw 指向 ro rw->ro = ro; // 設(shè)置 rw 的 flag 為 正在初始化和已經(jīng)初始化 /** // class is realized - must never be set by compiler #define RO_REALIZED (1<<31) // Values for class_rw_t->flags // These are not emitted by the compiler and are never used in class_ro_t. // Their presence should be considered in future ABI versions. // class_t->data is class_rw_t, not class_ro_t #define RW_REALIZED (1<<31) */ rw->flags = RW_REALIZED|RW_REALIZING; cls->setData(rw); } // 判斷是否為 metaClass RO_META (1 << 0) isMeta = ro->flags & RO_META; rw->version = isMeta ? 7 : 0; // old runtime went up to 6 // Choose an index for this class. // Sets cls->instancesRequireRawIsa if indexes no more indexes are available cls->chooseClassArrayIndex(); if (PrintConnecting) { _objc_inform("CLASS: realizing class '%s'%s %p %p #%u", cls->nameForLogging(), isMeta ? " (meta)" : "", (void*)cls, ro, cls->classArrayIndex()); } // Realize superclass and metaclass, if they aren't already. // This needs to be done after RW_REALIZED is set above, for root classes. // This needs to be done after class index is chosen, for root metaclasses. supercls = realizeClass(remapClass(cls->superclass)); metacls = realizeClass(remapClass(cls->ISA())); #if SUPPORT_NONPOINTER_ISA // Disable non-pointer isa for some classes and/or platforms. // Set instancesRequireRawIsa. bool instancesRequireRawIsa = cls->instancesRequireRawIsa(); bool rawIsaIsInherited = false; static bool hackedDispatch = false; if (DisableNonpointerIsa) { // Non-pointer isa disabled by environment or app SDK version instancesRequireRawIsa = true; } else if (!hackedDispatch && !(ro->flags & RO_META) && 0 == strcmp(ro->name, "OS_object")) { // hack for libdispatch et al - isa also acts as vtable pointer hackedDispatch = true; instancesRequireRawIsa = true; } else if (supercls && supercls->superclass && supercls->instancesRequireRawIsa()) { // This is also propagated by addSubclass() // but nonpointer isa setup needs it earlier. // Special case: instancesRequireRawIsa does not propagate // from root class to root metaclass instancesRequireRawIsa = true; rawIsaIsInherited = true; } if (instancesRequireRawIsa) { cls->setInstancesRequireRawIsa(rawIsaIsInherited); } // SUPPORT_NONPOINTER_ISA #endif // Update superclass and metaclass in case of remapping cls->superclass = supercls; cls->initClassIsa(metacls); // Reconcile instance variable offsets / layout. // This may reallocate class_ro_t, updating our ro variable. if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro); // Set fastInstanceSize if it wasn't set already. cls->setInstanceSize(ro->instanceSize); // Copy some flags from ro to rw if (ro->flags & RO_HAS_CXX_STRUCTORS) { cls->setHasCxxDtor(); if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) { cls->setHasCxxCtor(); } } // Connect this class to its superclass's subclass lists if (supercls) { addSubclass(supercls, cls); } else { addRootClass(cls); } // Attach categories // 在這個方法中將 rw 的方法列表耳高,屬性列表,協(xié)議列表賦值 methodizeClass(cls); return cls; };
此時所踊,經(jīng)過 Runtime 的作用之后泌枪,現(xiàn)在內(nèi)存中的關(guān)系是,類中的 data 指針指向 class_data_bits_t秕岛, class_data_bits_t 結(jié)構(gòu)體中的 data() 方法獲取到的是 class_rw_t 指針碌燕, class_rw_t 結(jié)構(gòu)體中的 ro 指針指向 class_ro_t。圖如下:
-
但是問題來了修壕,類的方法是如何被查找和調(diào)用的呢?由于我們已經(jīng)知道了遏考,在 ObjC 中慈鸠,實際上類也是一個特殊的對象,查找類的方法實際上就和查找實例方法采用同樣的機制灌具,但是如何才能讓他們采用同樣的機制呢青团?這時譬巫,元類的作用就顯現(xiàn)了出來。
- metaClass 保證了類中也有一個指向 Class 類型的指針督笆,保證了類和對象的一致性芦昔,保證了類查找方法的機制與對象查找方法的機制保持同步。
- 當實例方法調(diào)用時娃肿,通過對象的
isa
在類中獲取方法的實現(xiàn) - 當類方法調(diào)用時咕缎,通過類的
isa
在元類中獲取方法的實現(xiàn)
- 當實例方法調(diào)用時娃肿,通過對象的
- metaClass 保證了類中也有一個指向 Class 類型的指針督笆,保證了類和對象的一致性芦昔,保證了類查找方法的機制與對象查找方法的機制保持同步。
現(xiàn)在,我們的重點終于到了料扰, isa 到底是什么凭豪?
-
我們在 Runtime 的源碼中可以看到,在不同的處理器上,這個共同體所分配的內(nèi)存位數(shù)是不同的记罚。
union isa_t { isa_t() { } isa_t(uintptr_t value) : bits(value) { } Class cls; uintptr_t bits; #if SUPPORT_PACKED_ISA // extra_rc must be the MSB-most field (so it matches carry/overflow flags) // nonpointer must be the LSB (fixme or get rid of it) // shiftcls must occupy the same bits that a real class pointer would // bits + RC_ONE is equivalent to extra_rc + 1 // RC_HALF is the high bit of extra_rc (i.e. half of its range) // future expansion: // uintptr_t fast_rr : 1; // no r/r overrides // uintptr_t lock : 2; // lock for atomic property, @synch // uintptr_t extraBytes : 1; // allocated with extra bytes # if __arm64__ # define ISA_MASK 0x0000000ffffffff8ULL # define ISA_MAGIC_MASK 0x000003f000000001ULL # define ISA_MAGIC_VALUE 0x000001a000000001ULL struct { uintptr_t nonpointer : 1; uintptr_t has_assoc : 1; uintptr_t has_cxx_dtor : 1; uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000 uintptr_t magic : 6; uintptr_t weakly_referenced : 1; uintptr_t deallocating : 1; uintptr_t has_sidetable_rc : 1; uintptr_t extra_rc : 19; # define RC_ONE (1ULL<<45) # define RC_HALF (1ULL<<18) }; # elif __x86_64__ # define ISA_MASK 0x00007ffffffffff8ULL # define ISA_MAGIC_MASK 0x001f800000000001ULL # define ISA_MAGIC_VALUE 0x001d800000000001ULL struct { uintptr_t nonpointer : 1; uintptr_t has_assoc : 1; uintptr_t has_cxx_dtor : 1; uintptr_t shiftcls : 44; // MACH_VM_MAX_ADDRESS 0x7fffffe00000 uintptr_t magic : 6; uintptr_t weakly_referenced : 1; uintptr_t deallocating : 1; uintptr_t has_sidetable_rc : 1; uintptr_t extra_rc : 8; # define RC_ONE (1ULL<<56) # define RC_HALF (1ULL<<7) }; };
以 x86_64_ 為例墅诡,
-
更深一步桐智,從 isa 的初始化來看 isa 的結(jié)構(gòu)
inline void objc_object::initInstanceIsa(Class cls, bool hasCxxDtor) { initIsa(cls, true, hasCxxDtor); } inline void objc_object::initIsa(Class cls, bool indexed, bool hasCxxDtor) { if (!indexed) { isa.cls = cls; } else { isa.bits = ISA_MAGIC_VALUE; isa.has_cxx_dtor = hasCxxDtor; isa.shiftcls = (uintptr_t)cls >> 3; } } // 由于對象的 isa 初始化時傳入 indexed 為 true 末早,所以,可簡化為 inline void objc_object::initIsa(Class cls, bool indexed, bool hasCxxDtor) { // 其中 ISA_MAGIC_VALUE 為 0x000001a000000001ULL isa.bits = ISA_MAGIC_VALUE; isa.has_cxx_dtor = hasCxxDtor; isa.shiftcls = (uintptr_t)cls >> 3; }
-
此時说庭,當執(zhí)行完 isa.bits = ISA_MAGIC_VALUE; 后 isa 的結(jié)構(gòu)為 然磷,可以看到 ISA_MAGIC_VALUE 將 magic 和 indexed 都初始化了
接著 isa.has_cxx_dtor = hasCxxDtor; 這一位會設(shè)置 has_cxx_dtor 的值,如果是 1刊驴, 則表示當前對象是否有析構(gòu)器姿搜,如果沒有,就會快速釋放
最后捆憎, isa.shiftcls = (uintptr_t)cls >> 3; 將當前對象對應(yīng)的類指針賦值給 shiftcls 這些位舅柜,之所以向右移三位,移三位的主要原因是用于將 Class 指針中無用的后三位清楚減小內(nèi)存的消耗躲惰,因為類的指針要按照字節(jié)(8 bits)對齊內(nèi)存致份,其指針后三位都是沒有意義的 0。賦值之后如下
-
![image](http://upload-images.jianshu.io/upload_images/3262069-f3a02c34c16975e6?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
至此础拨,也就證明了我們之前對于初始化 `isa` 時對 `initIsa` 方法的分析是正確的氮块。它設(shè)置了 `indexed`、`magic` 以及 `shiftcls`诡宗。
獲取 isa
-
由于我們現(xiàn)在使用了結(jié)構(gòu)體 isa_t 來替代 Class 類型的指針滔蝉, 所以我們也就需要一個指針能夠返回 isa 所指的類,所以我們此時需要一個 ISA() 方法塔沃。
inline Class objc_object::ISA() { assert(!isTaggedPointer()); #if SUPPORT_INDEXED_ISA if (isa.nonpointer) { uintptr_t slot = isa.indexcls; return classForIndex((unsigned)slot); } return (Class)isa.bits; #else return (Class)(isa.bits & ISA_MASK); #endif } // 簡化后如下 inline Class objc_object::ISA() { assert(!isTaggedPointer()); // 由此可以看到蝠引,實際上 ISA() 返回的是 isa.bits 與 0x0000000ffffffff8ULL 進行的按位與操作,確實可以返回當前的類 return (Class)(isa.bits & ISA_MASK); }
總結(jié)
- 至此,此次源碼分析的第一部分也就此結(jié)束立肘,如果您發(fā)現(xiàn)了什么問題和不足歡迎與我探討和指教边坤。