一、回顧
oc對象的本質(zhì)就是一個結(jié)構(gòu)體,結(jié)構(gòu)體的內(nèi)部包含一個isa指針
指針:是一種保存變量地址的變量。
//OC
@interface NSObject{
Class isa; // 包含一個isa指針(地址值)
}
// 對應的 C
struct NSObject_IMPL{
Class isa;
}
//typedef起別名 Class是指向objc_class的指針
typedef struct objc_class *Class;
isa->Class->objc_class isa保存的地址存儲著obj_class結(jié)構(gòu)體
// isa 本質(zhì)就是一個指向 objc_class 結(jié)構(gòu)體的指針
二、實例對象的isa
實例對象保存了變量的值,沒有保存方法等其他的信息,那么實例對象調(diào)用方法的時候,是如何找到正確的方法呢?
isa 將類對象的地址保存到isa中 ,調(diào)用方法的時候,通過isa尋找到類對象,然后調(diào)用類對象中保存的方法
同理類對象的isa指向元類對象,可以獲取到元類對象中保存的類方法
三、元類對象的isa
元類對象后面只剩下基類的元類對象,所以每個元類對象的isa都是直接指向基類 即 NSObject 的元類對象,而基類的元類對象指向它自己。

image.png
四、舉例證明
// 實例化
Person *p = [[Person alloc] init];
// 獲取類對象地址
Class person_class = [Person class];
// 獲取元類對象的地址
Class person_meta_class = object_getClass(person_class);
// 打印p->isa
(lldb) p/x (long)p->isa //x表示轉(zhuǎn)化為十六進制
(long) $0 = 0x001d800100001149
// 打印 person_class地址,查看p->isa是否已person_class地址一致
(lldb) p/x person_class
(Class) $1 = 0x0000000100001148 Person
兩次打印結(jié)果不一致
在64位之后,isa的內(nèi)存地址需要&一個掩碼值,才能獲取到真正的內(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
這次確實兩次地址值一致
由此可見實例對象的isa確實是指向了類對象的內(nèi)存地址
若要證明 類對象的isa指向元類對象,需要自定義一個objc_class
struct pf_objc_class {
Class isa;
};
// 將類對象轉(zhuǎn)化為pf_objc_class結(jié)構(gòu)體,才能獲取到類對象的isa
struct pf_objc_class *person_class2 = (__bridge struct pf_objc_class *)(person_class);
// 類對象的isa地址
(lldb) p/x person_class2->isa
(Class) $5 = 0x001d800100001121
// 元類對象的實際地址
(lldb) p/x person_meta_class
(Class) $6 = 0x0000000100001120
// 同樣將類對象的isa值&掩碼值后等于元類對象的地址
(lldb) p/x 0x001d800100001121 & 0x00007ffffffffff8
(long) $7 = 0x0000000100001120
舉例證明isa變量的內(nèi)存地址,就是對象的內(nèi)存地址
(lldb) p p // 打印p的地址
(Person *) $9 = 0x0000000100541d80
(lldb) p &p->isa // 打印isa的地址(不是isa的值)
(__unsafe_unretained Class *) $7 = 0x0000000100541d80
/// 所謂的內(nèi)存地址,就是他們占用內(nèi)存的第一個位置
///如對象p占用內(nèi)存16個字節(jié),是從0x0000000100541d80開始,0x0000000100541d90結(jié)束,對象都有固定的長度,所以只需要記錄開始位置
/// 即 isa內(nèi)存起始位置就是對象p的起始位置
五、總結(jié)
- isa的內(nèi)存地址就是對象的內(nèi)存地址
- 實例對象的isa的值是類對象地址
- 類對象的isa的值是元類對象地址
- 元類對象的isa值是基類的元類對象地址,基類的元類對象isa的值是它自己的地址
思考:isa僅僅是保存地址這一個作用嗎?64位之后,優(yōu)化了什么?