一滔以、回顧
oc對(duì)象的本質(zhì)就是一個(gè)結(jié)構(gòu)體四濒,結(jié)構(gòu)體的內(nèi)部包含一個(gè)isa指針
指針:是一種保存變量地址的變量奴愉。
//OC
@interface NSObject{
Class isa; // 包含一個(gè)isa指針(地址值)
}
// 對(duì)應(yīng)的 C
struct NSObject_IMPL{
Class isa;
}
//typedef起別名 Class是指向objc_class的指針
typedef struct objc_class *Class;
isa->Class->objc_class isa保存的地址存儲(chǔ)著obj_class結(jié)構(gòu)體
// isa 本質(zhì)就是一個(gè)指向 objc_class 結(jié)構(gòu)體的指針
二蔬螟、實(shí)例對(duì)象的isa
實(shí)例對(duì)象保存了變量的值此迅,沒有保存方法等其他的信息,那么實(shí)例對(duì)象調(diào)用方法的時(shí)候旧巾,是如何找到正確的方法呢耸序?
isa 將類對(duì)象的地址保存到isa中 ,調(diào)用方法的時(shí)候鲁猩,通過isa尋找到類對(duì)象坎怪,然后調(diào)用類對(duì)象中保存的方法
同理類對(duì)象的isa指向元類對(duì)象,可以獲取到元類對(duì)象中保存的類方法
三廓握、元類對(duì)象的isa
元類對(duì)象后面只剩下基類的元類對(duì)象搅窿,所以每個(gè)元類對(duì)象的isa都是直接指向基類 即 NSObject 的元類對(duì)象,而基類的元類對(duì)象指向它自己隙券。
image.png
四男应、舉例證明
// 實(shí)例化
Person *p = [[Person alloc] init];
// 獲取類對(duì)象地址
Class person_class = [Person class];
// 獲取元類對(duì)象的地址
Class person_meta_class = object_getClass(person_class);
// 打印p->isa
(lldb) p/x (long)p->isa //x表示轉(zhuǎn)化為十六進(jìn)制
(long) $0 = 0x001d800100001149
// 打印 person_class地址,查看p->isa是否已person_class地址一致
(lldb) p/x person_class
(Class) $1 = 0x0000000100001148 Person
兩次打印結(jié)果不一致
在64位之后娱仔,isa的內(nèi)存地址需要&一個(gè)掩碼值殉了,才能獲取到真正的內(nèi)存地址
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
(lldb) p/x 0x001d800100001149 & 0x00007ffffffffff8
(long) $4 = 0x0000000100001148
(lldb) p/x person_class
(Class) $1 = 0x0000000100001148 Person
這次確實(shí)兩次地址值一致
由此可見實(shí)例對(duì)象的isa確實(shí)是指向了類對(duì)象的內(nèi)存地址
若要證明 類對(duì)象的isa指向元類對(duì)象,需要自定義一個(gè)objc_class
struct pf_objc_class {
Class isa;
};
// 將類對(duì)象轉(zhuǎn)化為pf_objc_class結(jié)構(gòu)體拟枚,才能獲取到類對(duì)象的isa
struct pf_objc_class *person_class2 = (__bridge struct pf_objc_class *)(person_class);
// 類對(duì)象的isa地址
(lldb) p/x person_class2->isa
(Class) $5 = 0x001d800100001121
// 元類對(duì)象的實(shí)際地址
(lldb) p/x person_meta_class
(Class) $6 = 0x0000000100001120
// 同樣將類對(duì)象的isa值&掩碼值后等于元類對(duì)象的地址
(lldb) p/x 0x001d800100001121 & 0x00007ffffffffff8
(long) $7 = 0x0000000100001120
舉例證明isa變量的內(nèi)存地址薪铜,就是對(duì)象的內(nèi)存地址
(lldb) p p // 打印p的地址
(Person *) $9 = 0x0000000100541d80
(lldb) p &p->isa // 打印isa的地址(不是isa的值)
(__unsafe_unretained Class *) $7 = 0x0000000100541d80
/// 所謂的內(nèi)存地址,就是他們占用內(nèi)存的第一個(gè)位置
///如對(duì)象p占用內(nèi)存16個(gè)字節(jié)恩溅,是從0x0000000100541d80開始隔箍,0x0000000100541d90結(jié)束,對(duì)象都有固定的長度脚乡,所以只需要記錄開始位置
/// 即 isa內(nèi)存起始位置就是對(duì)象p的起始位置
五蜒滩、總結(jié)
- isa的內(nèi)存地址就是對(duì)象的內(nèi)存地址
- 實(shí)例對(duì)象的isa的值是類對(duì)象地址
- 類對(duì)象的isa的值是元類對(duì)象地址
- 元類對(duì)象的isa值是基類的元類對(duì)象地址,基類的元類對(duì)象isa的值是它自己的地址
思考:isa僅僅是保存地址這一個(gè)作用嗎奶稠?64位之后俯艰,優(yōu)化了什么?