1、Runtime的定義
將源代碼轉(zhuǎn)換為可執(zhí)行的程序,通常要經(jīng)過(guò)三個(gè)步驟:編譯、鏈接、運(yùn)行。
Objective-C 語(yǔ)言 是一門(mén)動(dòng)態(tài)語(yǔ)言,在編譯階段并不知道變量的具體數(shù)據(jù)類型,也不知道所真正調(diào)用的哪個(gè)函數(shù)。只有在運(yùn)行時(shí)間才檢查變量的數(shù)據(jù)類型,同時(shí)在運(yùn)行時(shí)才會(huì)根據(jù)函數(shù)名查找要調(diào)用的具體函數(shù)。這樣在程序沒(méi)運(yùn)行的時(shí)候,我們并不知道調(diào)用一個(gè)方法具體會(huì)發(fā)生什么。
Objective-C 語(yǔ)言把一些決定性的工作從編譯階段、鏈接階段推遲到運(yùn)行時(shí)階段,使得 Objective-C 變得更加靈活。這也就說(shuō)只有編譯器是不夠的,還需要一個(gè)運(yùn)行時(shí)系統(tǒng) (runtime system) 來(lái)執(zhí)行編譯后的代碼。這就是 Objective-C Runtime 系統(tǒng)存在的意義。
Runtime 實(shí)際上是一個(gè)庫(kù),這個(gè)庫(kù)使我們可以在程序運(yùn)行時(shí)動(dòng)態(tài)的創(chuàng)建對(duì)象、檢查對(duì)象,修改類和對(duì)象的方法。
Runtime 基本是用 C 和匯編寫(xiě)的,可見(jiàn)蘋(píng)果為了動(dòng)態(tài)系統(tǒng)的高效而作出的努力。你可以在這里下到蘋(píng)果維護(hù)的開(kāi)源代碼。
2、與Runtime進(jìn)行交互
OC支持從三種不同的層級(jí)上與 Runtime 系統(tǒng)進(jìn)行交互,分別是通過(guò) Objective-C 源代碼,NSObject類定義的方法,Runtime 函數(shù)的直接調(diào)用。
Objective-C 源代碼
使用OC在編寫(xiě)代碼的時(shí)候,基本都是使用類、屬性、方法調(diào)用、協(xié)議、分類。但是在這些的背后,都是由runtime來(lái)支持的。我們平常所熟知的各種類型,背后都有runtime對(duì)應(yīng)的C語(yǔ)言結(jié)構(gòu)體,及C和匯編實(shí)現(xiàn)。比如類就被編譯成objc_class的結(jié)構(gòu)體,方法被編譯成method_t的結(jié)構(gòu)體等等。
NSObject 的方法
Cocoa 中大多數(shù)類都繼承于 NSObject 類,也就自然繼承了它的方法。作為大部分Objective-C類繼承體系的根類的NSObject,其本身就具有了一些非常具有運(yùn)行時(shí)動(dòng)態(tài)特性的方法,使用這些方法也算是一種與Runtme的交互方式,舉例如下:
//返回對(duì)象的類;
- (Class)class OBJC_SWIFT_UNAVAILABLE("use 'type(of: anObject)' instead");
//檢查對(duì)象對(duì)應(yīng)的類,與指定的類aClass及其父類進(jìn)行判斷。只要有相同的類,就判斷為true
- (BOOL)isKindOfClass:(Class)aClass;
//檢查對(duì)象所在的類,與指定的類aClass,如果一致就返回true
- (BOOL)isMemberOfClass:(Class)aClass;
//用來(lái)判斷對(duì)象是否實(shí)現(xiàn)了指定協(xié)議類的方法;
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;
//用來(lái)判斷對(duì)象能否響應(yīng)指定的消息;
- (BOOL)respondsToSelector:(SEL)aSelector;
2、Runtime 的函數(shù)
Runtime 系統(tǒng)是一個(gè)由一系列函數(shù)和數(shù)據(jù)結(jié)構(gòu)組成,具有公共接口的動(dòng)態(tài)共享庫(kù)。頭文件存放于/usr/include/objc目錄下。許多函數(shù)允許你用純C代碼來(lái)重復(fù)實(shí)現(xiàn) Objc 中同樣的功能。在文件中引入Runtime的頭文件#include <objc/runtime.h>,就可以使用Runtime的方法,并且適合OC程序是一樣的效果。舉例如下:
Man * m = [[Man alloc] init];
//OC語(yǔ)句
Class mClass = [m class];
//runtime函數(shù)
Class objClass = object_getClass(m);
NSLog(@"mClass = %@, objClass = %@",mClass,objClass);
//打印結(jié)果如下
//2022-05-31 09:40:14.506089+0800 schemeUse[1860:59157] mClass = Man, objClass = Man
3、Runtime中類的數(shù)據(jù)結(jié)構(gòu)
Objc 2.0之前的版本

Objc 2.0之后的版本

由OBJC 2.0后的版本的代碼可以看出:
-
Class類是一個(gè)objc_class *指針的別名 -
objc_class是繼承objc_object的結(jié)構(gòu)體 - 由于
objc_object有一個(gè)實(shí)例變量isa,并且objc_object是objc_class的superClass,則在objc_class必然是有isa實(shí)例變量。 - 在Runtime系統(tǒng)中,對(duì)象被定義為一個(gè)
objc_object類型的結(jié)構(gòu)體,所以Class也被看作是一類特殊的對(duì)象。 -
id是一種objc_object *的指針的別名
4、了解isa
從Objc 2.0以后的源代碼可以看出,所有的類都是繼承于objc_object,而objc_object中只有一個(gè)變量,即 isa_t類型的isa。也就是說(shuō)Objective-C 對(duì)象都是 C 語(yǔ)言結(jié)構(gòu)體,所有的對(duì)象都包含一個(gè)類型為 isa 的指針。所有與類相關(guān)的信息,都是在isa中進(jìn)行存儲(chǔ),比如說(shuō):是否指針優(yōu)化、是否有關(guān)聯(lián)對(duì)象、是否被弱引用等。
那么isa的作用是什么呢?
-
方法查詢機(jī)制。在Object-C中,對(duì)象的方法并不存儲(chǔ)在各個(gè)對(duì)象的內(nèi)存中(如果每一個(gè)對(duì)象都保存了自己能執(zhí)行的方法,那么對(duì)內(nèi)存的占用有極大的影響),而是保存在類的結(jié)構(gòu)體中(class_ro_t)。當(dāng)實(shí)例對(duì)象調(diào)用方法的時(shí)候,該對(duì)象要通過(guò)自己的
isa找到對(duì)應(yīng)的類,在類的方法列表(class_ro_t)中查找方法的實(shí)現(xiàn)。如果類的方法列表中不存在,就是用類的superClass去父類的方法列表繼續(xù)查找。
但是如果是類方法的調(diào)用,就會(huì)有不同。Objective-C為了在調(diào)用方法的時(shí)候,使得類方法和對(duì)象方法保持一致的方法查找機(jī)制,就引入了 元類的概念。每一個(gè)類的isa都指向自己的元類,在類中定義的類方法,存儲(chǔ)在元類中。在類方法的調(diào)用過(guò)程中,是根據(jù)isa查詢到自己的元類,在元類的方法列表中,查找類方法實(shí)現(xiàn)。
isa指向
isa和superClass指向.png 表明是否有關(guān)聯(lián)對(duì)象(has_assoc)。
表明是否有弱應(yīng)用(weakly_referenced)。
對(duì)象的引用計(jì)數(shù)信息(extra_rc)。
存儲(chǔ)對(duì)象的類的地址信息(shiftcls)。
isa的數(shù)據(jù)結(jié)構(gòu):
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)
};
# else
# error unknown architecture for packed isa
# endif
// SUPPORT_PACKED_ISA
#endif
#if SUPPORT_INDEXED_ISA
# if __ARM_ARCH_7K__ >= 2
# define ISA_INDEX_IS_NPI 1
# define ISA_INDEX_MASK 0x0001FFFC
# define ISA_INDEX_SHIFT 2
# define ISA_INDEX_BITS 15
# define ISA_INDEX_COUNT (1 << ISA_INDEX_BITS)
# define ISA_INDEX_MAGIC_MASK 0x001E0001
# define ISA_INDEX_MAGIC_VALUE 0x001C0001
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t indexcls : 15;
uintptr_t magic : 4;
uintptr_t has_cxx_dtor : 1;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 7;
# define RC_ONE (1ULL<<25)
# define RC_HALF (1ULL<<6)
};
# else
# error unknown architecture for indexed isa
# endif
// SUPPORT_INDEXED_ISA
#endif
};
可以看出isa_t 主要是有Class cls 和不同系統(tǒng)下的結(jié)構(gòu)體組成。在不同的系統(tǒng)下 ,結(jié)構(gòu)體的參數(shù)是一樣的,只是空間分配有所不同。這樣的話。這里以"__arm64__“進(jìn)行展示:
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
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;
};
};
isa_t是一個(gè)共用體union。
union:在一個(gè)“聯(lián)合”內(nèi)可以定義多種不同的數(shù)據(jù)類型, 一個(gè)該“聯(lián)合”類型的變量中,允許裝入該“聯(lián)合”所定義的任何一種數(shù)據(jù),這些數(shù)據(jù)共享同一段內(nèi)存,以達(dá)到節(jié)省空間的目的。這種“union”只能存放一種數(shù)據(jù),在isa_t中cls和結(jié)構(gòu)體只能使用一個(gè)變量。

示意圖

nonpointer
自2013年蘋(píng)果推出iphone5s之后,iOS的尋址空間擴(kuò)大到了64位。我們可以用63位來(lái)表示一個(gè)數(shù)字(一位做符號(hào)位)。那么這個(gè)數(shù)字的范圍是2^63 ,很明顯我們一般不會(huì)用到這么大的數(shù)字,那么在我們定義一個(gè)數(shù)字時(shí)NSNumber *num = @100,實(shí)際上內(nèi)存中浪費(fèi)了很多的內(nèi)存空間。
當(dāng)然蘋(píng)果肯定也認(rèn)識(shí)到了這個(gè)問(wèn)題,于是就引入了tagged pointer。tagged pointer是一種特殊的“指針”,其特殊在于,其實(shí)它存儲(chǔ)的并不是地址,而是真實(shí)的數(shù)據(jù)和一些附加的信息。
蘋(píng)果對(duì)于Tagged Pointer特點(diǎn)的介紹:

- Tagged Pointer專門(mén)用來(lái)存儲(chǔ)小的對(duì)象,例如NSNumber, NSDate, NSString。
- Tagged Pointer指針的值不再是地址了,而是真正的值。所以,實(shí)際上它不再是一個(gè)對(duì)象了,它只是一個(gè)披著對(duì)象皮的普通變量而已。所以,它的內(nèi)存并不存儲(chǔ)在堆中,也不需要malloc和free。
-
在內(nèi)存讀取上有著3倍的效率,創(chuàng)建時(shí)比以前快106倍。
tagged pointer.png
如上面tagged pointer所說(shuō),如果isa指針僅表示類型的話,對(duì)內(nèi)存顯然也是一個(gè)極大的浪費(fèi)。于是,就像tagged pointer一樣,對(duì)于isa指針,蘋(píng)果同樣進(jìn)行了優(yōu)化。isa指針表示的內(nèi)容變得更為豐富,除了表明對(duì)象屬于哪個(gè)類之外,還附加了引用計(jì)數(shù)extra_rc,是否有被weak引用標(biāo)志位weakly_referenced,是否有附加對(duì)象標(biāo)志位has_assoc等信息。
isa_t其實(shí)可以看做有兩個(gè)可能的取值,Class cls或struct。isa作為Class cls使用的時(shí)候,是我們一貫的認(rèn)知,存儲(chǔ)對(duì)應(yīng)的地址。這樣的話,內(nèi)存是有比較大的浪費(fèi)。所以絕大多數(shù)情況下,蘋(píng)果采用了優(yōu)化的isa策略,即,isa_t類型并不等同于Class cls, 而是struct。這在初始化isa的時(shí)候,表現(xiàn)很明顯。
有關(guān)聯(lián)對(duì)象標(biāo)志位has_assoc
NSObject * obj = [[NSObject alloc] init];
NSLog(@"isa_t = %p", *(void **)(__bridge void*)obj);
//2022-06-07 09:50:26.535944+0800 schemeUse[2530:174823] isa_t = 0x21a1dfe7f289
objc_setAssociatedObject(obj, "xxxx", self, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
NSLog(@"isa_t-associate = %p", *(void **)(__bridge void*)obj);
//2022-06-07 09:50:26.536051+0800 schemeUse[2530:174823] isa_t-associate = 0x21a1dfe7f28b
可以看出,添加關(guān)聯(lián)對(duì)象以后,isa是有變化的,全部轉(zhuǎn)成二進(jìn)制數(shù)據(jù)進(jìn)行對(duì)比:

對(duì)比上面“arm64”的示意圖,可以比較出來(lái),第二位數(shù)據(jù)變成了“1”,也就是
has_assoc位變化了。
被弱引用標(biāo)志位weakly_referenced
NSObject * obj = [[NSObject alloc] init];
NSLog(@"isa_t = %p", *(void **)(__bridge void*)obj);
//2022-06-07 10:13:45.789576+0800 schemeUse[2647:180952] isa_t = 0x21a1dfe7f289
__weak typeof(obj) weakObj = obj;
NSLog(@"isa_t-weak = %p", *(void **)(__bridge void*)obj);
//2022-06-07 10:13:45.789685+0800 schemeUse[2647:180952] isa_t-weak = 0x25a1dfe7f289
全部轉(zhuǎn)成二進(jìn)制數(shù)據(jù)進(jìn)行對(duì)比,可以看出第42位weakly_referenced位被置位1:

isa中的引用計(jì)數(shù)extra_rc
代碼:
NSObject * obj = [[NSObject alloc] init];
NSLog(@"refCount = %ld", (long)CFGetRetainCount((__bridge CFTypeRef)(obj)));
NSLog(@"isa_t—1 = %p", *(void **)(__bridge void*)obj);
//2022-06-08 10:56:14.495684+0800 schemeUse[6396:483268] refCount = 1
//2022-06-08 10:56:14.495792+0800 schemeUse[6396:483268] isa_t-refrence-1 = 0x21a1dfe7f289
NSObject * obj_refrence1 = obj;
NSLog(@"refCount = %ld", (long)CFGetRetainCount((__bridge CFTypeRef)(obj)));
NSLog(@"isa_t-refrence-2 = %p", *(void **)(__bridge void*)obj);
//2022-06-08 10:56:14.495831+0800 schemeUse[6396:483268] refCount = 2
//2022-06-08 10:56:14.495864+0800 schemeUse[6396:483268] isa_t-refrence-2 = 0x41a1dfe7f289
NSObject * obj_refrence2 = obj;
NSLog(@"refCount = %ld", (long)CFGetRetainCount((__bridge CFTypeRef)(obj)));
NSLog(@"isa_t-refrence-3 = %p", *(void **)(__bridge void*)obj);
//2022-06-08 10:56:14.495897+0800 schemeUse[6396:483268] refCount = 3
//2022-06-08 10:56:14.495928+0800 schemeUse[6396:483268] isa_t-refrence-3 = 0x61a1dfe7f289
全部轉(zhuǎn)成二進(jìn)制數(shù)據(jù)進(jìn)行對(duì)比,可以看出extra_rc的19位數(shù)據(jù)的變化(需要指出的是,如果has_sidetable_rc的標(biāo)識(shí)位為1,那么也就意味著extra_rc的19位二進(jìn)制數(shù)據(jù)裝不下對(duì)象的引用計(jì)數(shù),也就保存到了sidetable一部分。這樣的話引用計(jì)數(shù)就是兩部分加起來(lái)。):

對(duì)象的實(shí)例化過(guò)程中對(duì)isa的創(chuàng)建過(guò)程
1、類調(diào)用alloc方法時(shí),runtime系統(tǒng)內(nèi)部會(huì)調(diào)用_objc_rootAlloc(self);
2、而 _objc_rootAlloc方法中只是調(diào)用了callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);;
3、在callAlloc方法中,分配對(duì)象的空間以及初始化isa(initInstanceIsa(cls, dtor)方法)。
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
//其他代碼xxxxxxxx
bool dtor = cls->hasCxxDtor();
id obj = (id)calloc(1, cls->bits.fastInstanceSize());
if (slowpath(!obj)) return callBadAllocHandler(cls);
obj->initInstanceIsa(cls, dtor);
return obj;
//其他代碼zxxxxxxxxxx
}
4、initInstanceIsa 最終調(diào)用的是objc_object類的inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)方法
inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
//nonpointer的值,在initInstanceIsa方法中默認(rèn)傳值為true。initIsa(cls, true, hasCxxDtor);
if (!nonpointer) {
//如果沒(méi)沒(méi)有使用isa優(yōu)化,使用isa結(jié)構(gòu)體中的cls,來(lái)接受對(duì)象對(duì)應(yīng)的類信息。
isa.cls = cls;
} else {
//使用了isa優(yōu)化,創(chuàng)建一個(gè)新的isa,處理相關(guān)的信息,然后賦值給對(duì)象的isa
//對(duì)象實(shí)例化的時(shí)候,需要處理的isa信息包括:has_cxx_dtor、shiftcls等
isa_t newisa(0);
/**其他代碼**/
newisa.bits = ISA_MAGIC_VALUE;
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3;
isa = newisa;
}
}
對(duì)象的銷毀過(guò)程中,isa的作用
1、對(duì)象銷毀,會(huì)調(diào)用dealloc方法,該方法內(nèi)部調(diào)用的是_objc_rootDealloc(self);
2、_objc_rootDealloc方法內(nèi)部,調(diào)用起了obj->rootDealloc();
3、 rootDealloc方法中,主要是對(duì)isa的信息進(jìn)行判斷,檢查是否可以直接free對(duì)象
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
if (fastpath(isa.nonpointer && //對(duì)象的isa是有優(yōu)化的
!isa.weakly_referenced && //對(duì)象沒(méi)有被弱引用
!isa.has_assoc && // 不存在關(guān)聯(lián)對(duì)象
!isa.has_cxx_dtor && // 沒(méi)有 有C++的析構(gòu)函數(shù)
!isa.has_sidetable_rc)) //在sidetable中不存在引用計(jì)數(shù)
{
assert(!sidetable_present());
//直接free
free(this);
}
else {
//需要去處理isa中相關(guān)的信息
object_dispose((id)this);
}
}
4、在 object_dispose方法中,調(diào)用objc_destructInstance(obj),清理isa中的相關(guān)信息,弱引用、關(guān)聯(lián)對(duì)象、c++析構(gòu)函數(shù)、引用計(jì)數(shù)等,清理完成以后,釋放對(duì)象。
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor(); //獲取isa中C++標(biāo)志位的信息
bool assoc = obj->hasAssociatedObjects(); //獲取isa中關(guān)聯(lián)對(duì)象的信息
// This order is important.
if (cxx) object_cxxDestruct(obj); //C++標(biāo)志位有數(shù)據(jù),處理析構(gòu)
if (assoc) _object_remove_assocations(obj); // 清理關(guān)聯(lián)的對(duì)象
obj->clearDeallocating(); //清理弱引用、sidetable的引用計(jì)數(shù)
}
return obj;
}
//清理弱引用、sidetable的引用計(jì)數(shù)
inline void objc_object::clearDeallocating()
{
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
}
else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
// Slow path for non-pointer isa with weak refs and/or side table data.
clearDeallocating_slow();
}
assert(!sidetable_present());
}
5、類的結(jié)構(gòu)
在runtime系統(tǒng)中,objc_class是類的結(jié)構(gòu)體,objc_class繼承于objc_object。
struct objc_class : objc_object {
// Class ISA;
Class superclass; //父類
cache_t cache; // formerly cache pointer and vtable // 方法緩存
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags //存儲(chǔ)類的信息,成員變量,方法,協(xié)議等
//可以使用data通過(guò)一些與或操作,獲取到成員變量、方法、協(xié)議等
class_rw_t *data() {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
//初始化,以及獲取isa、類的信息的一些方法
///xxxxxxxx
};
可以看出,在objc_class中,有三個(gè)數(shù)據(jù):Class superclass、cache_t cache、class_data_bits_t bits。
Class superclass:表明當(dāng)前類的父類,其本身也是一個(gè)Class類型。
- 根元類的父類,是
NSObject。 -
NSObject的父類是nil。
cache_t cache: cache的作用在于緩存方法, 方法調(diào)用的時(shí)候,可以更快速的找到方法的IMP。
cache的數(shù)據(jù)結(jié)構(gòu)為:
//方法緩存的結(jié)構(gòu),共占用16個(gè)字節(jié)
struct cache_t {
private:
//8個(gè)字節(jié),存放方法的空間首地址信息(low 48位)和mask信息(high 16位),
explicit_atomic<uintptr_t> _bucketsAndMaybeMask;
union {
struct {
//_maybeMask is the buckets mask
explicit_atomic<mask_t> _maybeMask; //4個(gè)字節(jié),存儲(chǔ)的時(shí)候是newCapacity - 1
#if __LP64__
uint16_t _flags;
#endif
//緩存的方法數(shù)量
uint16_t _occupied;
};
explicit_atomic<preopt_cache_t *> _originalPreoptCache;
};
//向緩存中添加方法
void insert(SEL sel, IMP imp, id receiver);
//取緩存方法列表
struct bucket_t *buckets() const;
//可以緩存方法的空間數(shù)量
unsigned capacity() const;
//已經(jīng)緩存的方法數(shù)量
mask_t occupied() const;
//增加已緩存方法的數(shù)量
void incrementOccupied();
//設(shè)置_bucketsAndMayBeMask
void setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask);
//開(kāi)辟緩存的空間
void reallocate(mask_t oldCapacity, mask_t newCapacity, bool freeOld);
/**其他方法**/
};
cahce_t是一個(gè)結(jié)構(gòu)體,共計(jì)16個(gè)字節(jié)的大小。包含兩個(gè)成員,一個(gè)是_bucketsAndMaybeMask,一個(gè)是union聯(lián)合體,各占8個(gè)字節(jié)。cache_t中的方法主要圍繞這兩個(gè)成員的操作進(jìn)行的。
_bucketsAndMaybeMask:實(shí)際上是一塊內(nèi)存的首地址。方法緩存主要是以buckets(bucket_t)進(jìn)行存儲(chǔ),存儲(chǔ)內(nèi)容就是SEL和IMP。_bucketsAndMaybeMask通過(guò)和 mask的與操作,就能獲得緩存的方法列表。
struct bucket_t *cache_t::buckets() const
{
uintptr_t addr = _bucketsAndMaybeMask.load(memory_order_relaxed);
return (bucket_t *)(addr & bucketsMask);
}
方法的緩存過(guò)程:
方法緩存調(diào)用的是void insert(SEL sel, IMP imp, id receiver);方法。該方法要做的事情分為三塊:
- 開(kāi)辟緩存空間
- 緩存空間的擴(kuò)容
- 方法插入緩存
void cache_t::insert(SEL sel, IMP imp, id receiver)
{
//1.開(kāi)辟緩存空間
// Use the cache as-is if until we exceed our expected fill ratio.
mask_t newOccupied = occupied() + 1;
unsigned oldCapacity = capacity(), capacity = oldCapacity;
if (slowpath(isConstantEmptyCache())) {
// Cache is read-only. Replace it.
//如果緩存里面為空。先分配4個(gè)容量大小的空間。并調(diào)用reallocate去開(kāi)辟空間
if (!capacity) capacity = INIT_CACHE_SIZE; // 1<<2
//開(kāi)辟空間,并且給_bucketsAndMayBemask、_maybemask賦值
reallocate(oldCapacity, capacity, /* freeOld */false);
}
//2、緩存空間的擴(kuò)容
else if (fastpath(newOccupied + CACHE_END_MARKER <= cache_fill_ratio(capacity))) {
// Cache is less than 3/4 or 7/8 full. Use it as-is.
}
#if CACHE_ALLOW_FULL_UTILIZATION
else if (capacity <= FULL_UTILIZATION_CACHE_SIZE && newOccupied + CACHE_END_MARKER <= capacity) {
// Allow 100% cache utilization for small buckets. Use it as-is.
}
#endif
else {
capacity = capacity ? capacity * 2 : INIT_CACHE_SIZE;
if (capacity > MAX_CACHE_SIZE) {
capacity = MAX_CACHE_SIZE;
}
reallocate(oldCapacity, capacity, true);
}
//3、方法插入緩存,方法插入緩存的代碼
bucket_t *b = buckets();
mask_t m = capacity - 1;//3
mask_t begin = cache_hash(sel, m);
mask_t i = begin;
// Scan for the first unused slot and insert there.
// There is guaranteed to be an empty slot.
do {
if (fastpath(b[i].sel() == 0)) {
incrementOccupied();// _occupied++;
b[i].set<Atomic, Encoded>(b, sel, imp, cls());
return;
}
if (b[i].sel() == sel) {
// The entry was added to the cache by some other thread
// before we grabbed the cacheUpdateLock.
return;
}
} while (fastpath((i = cache_next(i, m)) != begin));
bad_cache(receiver, (SEL)sel);
#endif // !DEBUG_TASK_THREADS
}
消息機(jī)制中,方法的緩存流程:
class_data_bits_t bits: 保存類的成員變量、成員方法、協(xié)議、屬性
在之前isa的學(xué)習(xí)中,討論過(guò)對(duì)象的方法不存儲(chǔ)在各個(gè)對(duì)象的內(nèi)存中,而是保存在類的結(jié)構(gòu)體中。具體保存類結(jié)構(gòu)的什么位置呢?就是class_data_bits_t bits。使用該成員可以獲取到一個(gè)類的成員變量、成員方法、協(xié)議、屬性等信息。
class_data_bits_t數(shù)據(jù)結(jié)構(gòu)如下:
struct class_data_bits_t {
// Values are the FAST_ flags above.
uintptr_t bits;
public:
//類的成員變量、成員方法、協(xié)議、屬性(包含分類創(chuàng)建的方法)
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
void setData(class_rw_t *newData)
{
ASSERT(!data() || (newData->flags & (RW_REALIZING | RW_FUTURE)));
// Set during realization or construction only. No locking needed.
// Use a store-release fence because there may be concurrent
// readers of data and data's contents.
uintptr_t newBits = (bits & ~FAST_DATA_MASK) | (uintptr_t)newData;
atomic_thread_fence(memory_order_release);
bits = newBits;
}
// Get the class's ro data, even in the presence of concurrent realization.
// fixme this isn't really safe without a compiler barrier at least
// and probably a memory barrier when realizeClass changes the data field
//類的成員變量、成員方法、協(xié)議、屬性(只是在類中定義的,并不包含分類中的方法)
const class_ro_t *safe_ro() const {
class_rw_t *maybe_rw = data();
if (maybe_rw->flags & RW_REALIZED) {
// maybe_rw is rw
return maybe_rw->ro();
} else {
// maybe_rw is actually ro
return (class_ro_t *)maybe_rw;
}
}
/*其他方法*/
};
這個(gè)結(jié)構(gòu)體是一個(gè)64bit的成員變量bits,先來(lái)看看這64bit分別存放的什么信息:

可以看出數(shù)據(jù)的第4-48位,是存放一個(gè)指向class_rw_t結(jié)構(gòu)體的指針,該結(jié)構(gòu)體包含了該類的屬性,方法,協(xié)議等信息,是
class_data_bits_t bits的核心。下面就來(lái)說(shuō)說(shuō)核心部分:
class_rw_t和class_ro_t
class_ro_t說(shuō)明:1、該屬性是在編譯期就生成的,它存儲(chǔ)的內(nèi)容包含了當(dāng)前類的名稱,以及method_list_t, protocol_list_t, ivar_list_t, property_list_t 這些類的基本信息,在class_ro_t里面是沒(méi)有分類中定義的方法和協(xié)議的。
class_rw_t說(shuō)明:
1、是class_ro_t外面的一層,在運(yùn)行時(shí)生成的。在_objc_init方法中關(guān)于dyld的回調(diào)的map_images中,調(diào)用realizeClass方法,該方法最終將類和分類的所有方法、協(xié)議都插入到自己的方法列表、協(xié)議列表中。
2、它不包含成員變量列表,因?yàn)槌蓡T變量列表是在編譯期就確定好的,它只保存在class_ro_t中。
3、成員方法會(huì)保存在class_rw_t中。
4、class_rw_t中包含了一個(gè)指向class_ro_t的指針,可以方便的讀取類的成員變量信息。
在調(diào)用realizeClass方法之前,類的 class_data_bits_t是指向class_ro_t結(jié)構(gòu)體;調(diào)用realizeClass方法之后,class_data_bits_t是指向class_rw_t結(jié)構(gòu)體,把分類中的方法匯總到class_rw_t中。并且class_rw_t會(huì)有一個(gè)變量指向class_ro_t。

細(xì)看兩個(gè)結(jié)構(gòu)體的成員變量會(huì)發(fā)現(xiàn)很多相同的地方,他們都存放著當(dāng)前類的屬性、實(shí)例變量、方法、協(xié)議等等。區(qū)別在于:class_ro_t存放的是編譯期間就確定的;而class_rw_t是在runtime時(shí)才確定,它會(huì)先將class_ro_t的內(nèi)容拷貝過(guò)去,然后再將當(dāng)前類的分類的這些屬性、方法等拷貝到其中。所以可以說(shuō)class_rw_t是class_ro_t的超集,當(dāng)然實(shí)際訪問(wèn)類的方法、屬性等也都是訪問(wèn)的class_rw_t中的內(nèi)容 。
6、類的總結(jié)圖示
類的大?。?/p>

類的結(jié)構(gòu)總結(jié)圖:


