isa的作用與內(nèi)部結(jié)構(gòu)(上)

一、回顧

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é)

  1. isa的內(nèi)存地址就是對象的內(nèi)存地址
  2. 實例對象的isa的值是類對象地址
  3. 類對象的isa的值是元類對象地址
  4. 元類對象的isa值是基類的元類對象地址,基類的元類對象isa的值是它自己的地址
思考:isa僅僅是保存地址這一個作用嗎?64位之后,優(yōu)化了什么?

isa的作用與內(nèi)部結(jié)構(gòu)(下)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容