前言
之前我們已經探索得出對象的本質就是一個帶有isa指針的結構體,這篇文章來分析一下類的結構以及類的底層原理。
類的本質
類的本質
我們在main函數中寫入以上代碼,然后利用clang對其進行反編譯,可以得到c++文件
可以看到底層使用Class接收,接下來找到Class的定義
發現Class在底層定義為一個名為objc_class的結構體
查找objc_class,發現它繼承自objc_object
可以得出:類的本質是objc_class類型的結構體,objc_class繼承自objc_object,因此可以一句話概括——萬物皆對象
objc_class&objc_object、objc&NSObject的關系
objc_object和NSObject
為什么說繼承自objc_object就滿足萬物皆對象?
我們看到NSObject的定義
對比objc_object的定義:
仔細比較可以看出:
-
其實NSObject是objc_object的仿寫,和objc_object的定義是一樣的,在底層會編譯成objc_object
-
同理NSObject類是OC版本的objc_class
而之前我們學習過的NSObject_IMPL則是NSObject編譯到objc_object的中間產物,并且實例偽繼承自NSObject_IMPL,正如繼承objc_object的皆為對象。
objc_class&objc_object
從上文我們已經得知:objc_class繼承自objc_object,我們尋找objc_object的定義
可以找到兩個定義:第一個位于objc.h,沒有被廢除,從編譯的main-arm64.cpp中可以看到,使用的這個版本的objc_object。第二個位于objc-privat.h
總結objc_class與objc_object的關系如下:
-
結構體類型objc_class繼承自objc_object類型,其中objc_object也是一個結構體,且有一個isa屬性,所以objc_class也擁有了isa屬性
-
mian-arm64.cpp底層編譯文件中,NSObject中的isa在底層是由Class 定義的,其中class的底層編碼來自 objc_class類型,所以NSObject也擁有了isa屬性
-
NSObject是一個類,用它初始化一個實例對象objc,objc 滿足objc_object的特性(即有isa屬性),主要是因為isa 是由 NSObject 從objc_class繼承過來的,而objc_class繼承自objc_object,objc_object 有isa屬性。所以對象都有一個isa,isa表示指向,來自于當前的objc_object
-
objc_object(結構體)是當前的根對象,所有的對象都有這樣一個特性objc_object,即擁有isa屬性
objc_object 與 對象的關系
所有的
對象
都是以objc_object
為模板繼承
過來的所有的對象是來自
NSObject(OC)
,但是真正到底層的是一個objc_object(C/C++)
的結構體類型
objc_object
與對象
的關系 是繼承關系
總結
所有的對象 + 類 + 元類 都有isa屬性
所有的對象
都是由objc_object繼承來的
簡單概括就是萬物皆對象,萬物皆來源于objc_object,有以下兩點結論:
-
所有以 objc_object為模板創建的對象,都有isa屬性
-
所有以 objc_class為模板創建的類,都有isa屬性
在結構層面可以通俗的理解為上層OC與底層的對接:
-
下層是通過結構體定義的模板,例如objc_class、objc_object
-
上層是通過底層的模板創建的一些類型,例如TCJPerson
objc_class、objc_object、isa、object、NSObject等的整體的關系,如下圖:
類的結構
從objc_class的定義可以得出,類有4個屬性:
-
Class ISA:類對象與實例對象一樣,同樣有isa指針,類對象的isa指針關聯著元類,Class本身就是一個指針,占用8字節(這個屬性是繼承自objc_object的)
-
Class superclass:即類的父類,Class類型,占用8個字節
-
cache_t cache
cache_t是一個結構體,內存長度由所有元素決定:_bucketsAndMaybeMask是long類型,它是一個指針,占用8字節; mask_t是個uint32_t類型,_mask占用4字節;因_occupied和_flags都是uint16_t類型,uint16_t是 unsigned short 的別名,所以_occupied占用2字節;flags占用2字節=>cache_t占用16字節。這個屬性其實是用來保存方法緩存的,后續博客中會詳細介紹。
-
class_data_bits_t bits
class_data_bits_t bits
class_data_bits_t bits這個屬性用來存數據,類的屬性和方法就保存在這里。可以看到objc_class中有一個class_rw_t *data()方法。
class_rw_t是在運行時生成的,它在realizeClass中生成,它包含了class_ro_t.它在_objc_init方法中關于dyld的回調的map_images中最終將分類的方法與協議都插入到自己的方法列表、協議列表中.它不包含成員變量列表,因為成員變量列表是在編譯期就確定好的,它只保存在class_ro_t中.不過,class_rw_t中包含了一個指向class_ro_t的指針
類的屬性方法
類的屬性
通過LLDB調試我們可以發現,bits當中存儲的信息類型是class_rw_t,是一個結構體類型,在這個結構體中有提供相應的方法去獲取屬性列表、方法列表等。
通過LLDB進一步調試可以發現,這個屬性列表中只有屬性,沒有成員變量。屬性與成員變量的區別就是有沒有set、get方法,如果有,則是屬性,如果沒有,則是成員變量。
除此之外,class_rw_t中有個屬性class_ro_t,class_ro_t是在編譯期生成的,它存儲了當前類在編譯期就已經確定的屬性、方法以及協議,它里面沒有分類中定義的方法和協議。而成員變量就存放在ro的ivars里面
總結如下:
-
通過{}定義的成員變量,會存儲在類的bits屬性中,通過bits --> data() -->ro() --> ivars獲取成員變量列表,除了包括成員變量,還包括屬性定義的成員變量
-
通過@property定義的屬性,也會存儲在bits屬性中,通過bits --> data() --> properties() --> list獲取屬性列表,其中只包含屬性
類的方法
類的實例方法存儲在類的bits屬性中,通過bits --> methods() --> list獲取實例方法列表,參觀游客實例方法,方法列表中還包含屬性的set方法和get方法,以及系統在底層添加的C++的.cxx_destruct方法。
類的類方法存儲在元類的bits屬性中,通過元類bits --> methods() --> list獲取
類方法列表。
結論
-
成員變量存放在ivar
-
屬性存放在property,同時也會存一份在ivar,并生成setter、getter方法
-
對象方法存放在類里面
-
類方法存放在元類里面
補充
類存在幾份
由于類的信息在內存中永遠只存在一份,所以 類對象只有一份。
isKindOfClass和isMerberOfClass的理解
對于isMerberOfClass,方法會對比元類或類本身與某個類(元類還是類取決于類方法還是實例方法,總之就是取isa指針指向的東西)
而對于isKindOfClass,走以下邏輯:
最后給出一張isa的走位圖,實現為superclass指向,虛線為isa指向