前言
相信大部分多年工作經驗的iOS開發(fā)者知道了OC的對象本質上是一個基于C語言封裝的結構體。這個結構體有一個成員叫isa,它指向這個對象的類對象,那么今天我就來深入探究下這個isa成員。
驗證對象是否真的是結構體,結構體是否有isa成員。
1.我們創(chuàng)建一個工程,在man.m文件內定義一個繼承NSObject的對象LGPerson,里面放一個NSString屬性name。如下圖

main.m
2.用Clang編譯器將剛剛編寫的main.m文件編譯成C++文件。
用終端cd到剛剛創(chuàng)建的工程根目錄下,然后執(zhí)行clang -rewrite-objc main.m -o main.cpp把目標文件編譯成c++文件

編譯成C++
然后我們打開main.cpp文件,搜索LGPerson結果如下,我們發(fā)現了LGPerson真的是個結構體,下圖紅框標注iOS底層聲明了一個
objc_object結構體LGPerson,我們記住這個objc_object結構體名稱。
LGPerson
3.我們在main.m文件導入頭文件#import <objc/objc.h>,按住command鼠標左鍵點擊頭文件跳轉進去objc源碼,然后我們搜索objc_object,發(fā)現如下圖所示iOS底層對象真的是結構體封裝,并且真的里面有一個isa。

objc文件源碼
isa里面有哪些信息,是不是真的有類對象,還有沒有其他信息,我們接下來看看
1.我們繼續(xù)打開之前博客提到的objc源碼工程,我們看到紅框里面isa是一個union聯(lián)合體。
結構體(struct)中所有變量是“共存”的——優(yōu)點是“有容乃大”, 全面;缺點是struct內存空間的分配是粗放的,不管用不用,全分配。
聯(lián)合體(union)中是各變量是“互斥”的——缺點就是不夠“包容”; 但優(yōu)點是內存使用更為精細靈活,也節(jié)省了內存空間。

isa結構
大家可以具體了解下聯(lián)合體概念,接下來我們點擊聯(lián)合體
isa里面的ISA_BITFIELD看到這是一個宏定義
isa
那么上面宏定義里面這些
nonpointer :1是代表什么呢?nonpointer:表示是否對 isa 指針開啟指針優(yōu)化 0:純isa指針,1:不止是類對象地址,isa 中包含了類信息、對象的引用計數等。has_assoc:關聯(lián)對象標志位,0沒有,1存在。has_cxx_dtor:該對象是否有 C++ 或者 Objc 的析構器,如果有析構函數,則需要做析構邏輯, 如果沒有,則可以更快的釋放對象。shiftcls:存儲類指針的值。開啟指針優(yōu)化的情況下,在 arm64 架構中有 33 位用來存儲類指針,上面圖中74行# elif __x86_64__表示在x86_64(模擬器,Mac端)內核下占44位。magic:用于調試器判斷當前對象是真的對象還是沒有初始化的空間。weakly_referenced:志對象是否被指向或者曾經指向一個 ARC 的弱變量,沒有弱引用的對象可以更快釋放。
deallocating:標志對象是否正在釋放內存。has_sidetable_rc:當對象引用技術大于 10 時,則需要借用該變量存儲進位。extra_rc:當表示該對象的引用計數值,實際上是引用計數值減 1, 例如,如果對象的引用計數為 10,那么 extra_rc 為 9。如果引用計數大于 10, 則需要使用到下面的 has_sidetable_rc。isa占8字節(jié)64位,第一個
nonpointer :1,表示從右開始長度為1的字節(jié)位數信息為nonpointer同理第四排
shiftcls :44表示從第4位開始往左長度為44位存儲類指針好了,看完資料我們了解了isa里面第4位到47位存儲了類指針,接下來就是驗證環(huán)節(jié)了,注意我們是在模擬器運行,如果是真機運行那么
shiftcls就只占33位了。
image.png
我們打個斷點然后在控制臺按照紅框的指令打印LGPerson對象p的數據,最后拿到p的首內存地址數據
0x001d800100002205(也就是isa字段),然后我們通過輸入p/t LGPerson.class打印LGPerson類所存儲的2進制地址。
isa內4-47位數據和LGPerson類存儲指針2進制地址
我們發(fā)現一個驚人的事實,那就是我紅框內的數據一模一樣!從右起第4位開始到第47位中間44位數據一模一樣!這個完全驗證了我們上面貼圖的
isa內的shiftcls : 44;的定義。
結論
對象其實是底層封裝好的結構體,結構體內第一個成員為isa(這個對象所繼承的類指針),其中該類指針放在isa的第4-47位(x86_64 模擬器)或第4-37位(arm64 真機)