iOS底層-類的底層原理(二)

前言

類的底層原理(一) 的探索后,已理解 isa指針指向類的結(jié)構(gòu) 。下面繼續(xù)探索類的底層原理,并做相應(yīng)的補(bǔ)充。

準(zhǔn)備工作

WWDC-關(guān)于 runtime 的改進(jìn)優(yōu)化

LLVM源碼

成員變量的底層原理

在分析 類的底層原理(一) 時(shí),只分析了 propertiesmethods。

  • propertiesmethods 都在 class_rw_t 中。

  • 而成員變量 ivars 存在于class_rw_tro()中,也就是 class_ro_t 。

class_ro_t 結(jié)構(gòu)如下:

案例一

通過案例分析 ivars ,添加如下代碼 :

打印結(jié)果:

此時(shí),ivars 都存在于 ivar_list_t 中,使用C++數(shù)組 get() 獲取每個(gè)成員變量。

成員變量、屬性、實(shí)例變量的區(qū)別

  • 成員變量在類的 {} 中,以 基本數(shù)據(jù)類型 聲明的變量,例如:NSString、int、float、double、char、bool。

  • 屬性是用 @property 修飾的,在底層會(huì)變成 _ 方式的 成員變量 ,也會(huì)自動(dòng)生成 getset 方法。 屬性 = _成員變量 + set + get

  • 實(shí)例變量是以 對(duì)象類型 聲明的 (特殊的成員變量),例如 NSObject *p,p 就是實(shí)例變量。

案例二

1. 創(chuàng)建一個(gè) Project,添加如下代碼:

2.通過 clang 編譯并查看編譯文件:

$ clang -rewrite-objc main.m -o main.cpp

屬性在編譯時(shí)會(huì)變成 _的成員變量,并生成對(duì)應(yīng)的 setget 方法。

但是其 set 實(shí)現(xiàn)方式卻不一樣:

  • name 是通過 objc_setProperty 方法實(shí)現(xiàn)的

  • addressage 則是通過內(nèi)存地址偏移的方式存儲(chǔ)的

為什么會(huì)不一樣?namecopy 修飾,猜想是和 copy 修飾符有關(guān)。

分析 objc_setProperty

為什么會(huì)有 objc_setProperty 的存在?

當(dāng)創(chuàng)建一個(gè) 屬性 時(shí),調(diào)用 set 存數(shù)據(jù)時(shí),不可能每創(chuàng)建一個(gè) 屬性 就在底層生成一個(gè)對(duì)應(yīng)的 set 方法,這樣對(duì)內(nèi)存開銷太大了。于是就有了 objc_setProperty 方法,不管上層是什么 set 方法,統(tǒng)一調(diào)用 objc_setProperty 方法。這個(gè)過程就是 SELIMP 過程。

SEL 就是方法名字,IMP 就是底層方法實(shí)現(xiàn)。

由于 objc_setProperty 是需要在編譯時(shí)直接創(chuàng)建,所以 objc_setProperty 需要去 LLVM源碼 中查找。

1. 定位 objc_setProperty 方法

2. 定位 getSetPropertyFn 方法

3. 定位 GetPropertySetFunction 方法

GetPropertySetFunction 方法調(diào)用,是在 switch 選擇 strategy.getKind()PropertyImplStrategy::GetSetProperty 或者 PropertyImplStrategy::SetPropertyAndExpressionGet 時(shí)執(zhí)行的。

那么 strategy 是什么意思?什么時(shí)候給它賦值?每個(gè) case 都有什么意義?

4. 分析 PropertyImplStrategy

5. PropertyImplStrategy 構(gòu)造函數(shù)

結(jié)論:

  • 只要是設(shè)置了 copy 屬性,不管是不是原子性,都沒有影響,set 方法都會(huì)被重定向到objc_setProperty 。

  • 如果不設(shè)置屬性(除原子性之外),那么默認(rèn)屬性是 strong ,不會(huì)觸發(fā) objc_setProperty。

分析 objc_getProperty

同樣 objc_getProperty 也需要去 LLVM源碼 中查找。

1. 定位 objc_getProperty 方法

2. 定位 getGetPropertyFn 方法

3. 定位 GetPropertyGetFunction 方法

類方法的底層原理

lldb 驗(yàn)證流程如下:

總結(jié):

  • 對(duì)象方法 存儲(chǔ)在自身

  • 類方法 存儲(chǔ)在 元類 中,并且以 對(duì)象方法 存在于 元類 中。

  • 沒有所謂的 類方法 之說(shuō),所有的方法都是 對(duì)象方法,其底層都是 函數(shù)

補(bǔ)充:類型編碼 TypeEncoding

在上面的 main.cpp 中,搜索 setName 發(fā)現(xiàn)有一些奇奇怪怪的符號(hào)。

其中 "v24@0:8@16"、"@16@0:8" 其實(shí)是 類型編碼 。下面具體看一下什么是 類型編碼。

官網(wǎng)關(guān)于 TypeEncoding 的解釋。歸根結(jié)底,其實(shí)就是對(duì)照著下面的 符號(hào)表 去分析編碼代碼。

TypeEncoding符號(hào)表

那么以上面 setName 為例,具體分析 v24@0:8@16 的實(shí)際意義。

補(bǔ)充:面試題

1. 為什么獲取 元類類方法 也可以得到 類方法?(已證明:類方法對(duì)象方法 形式存儲(chǔ)在 元類 中)

打印結(jié)果如下:

分析底層方法:

分析得出:所謂的 類方法 其實(shí)就是獲取 元類對(duì)象方法

分析 getMeta() 方法:

分析得出:如果是 元類 ,則返回 元類本身 ,否則返回 元類isa。這就是為什么獲取 元類類方法 也可以得到 類方法的原因。

2. 關(guān)于 isKindOfClass

案例分析:

打印結(jié)果:

并且也在源碼中找到 isMemberOfClassisKindOfClass 方法,也打上斷點(diǎn),來(lái)具體分析其原理:

但是執(zhí)行過程中,卻沒有執(zhí)行 isKindOfClass 方法,只執(zhí)行了 isMemberOfClass 方法。先來(lái)分析 isMemberOfClass

isMemberOfClass

分析源碼如下:

  • + isMemberOfClass: 是獲取類的 元類 進(jìn)行比較

  • - isMemberOfClass: 是獲取 類對(duì)象 進(jìn)行比較

分析上圖代碼:

  • re1NSObject 調(diào)用 + isMemberOfClass:NSObject 比較。因此 NSObject元類NSObject 并不相等,所以是0。
  • re3ZLObject 調(diào)用 + isMemberOfClass:ZLObject 比較。因此 ZLObject元類ZLObject 并不相等,所以是0。
  • re5NSObject對(duì)象 調(diào)用 - isMemberOfClass:NSObject 比較。因此 NSObject對(duì)象 的類是NSObject,與 NSObject 相等,所以是1。
  • re7ZLObject對(duì)象 調(diào)用 - isMemberOfClass:ZLObject 比較。因此 ZLObject對(duì)象 的類是ZLObject,與 ZLObject 相等,所以是1。

isKindOfClass

上述案例中,isKindOfClass 沒有執(zhí)行,只有 isMemberOfClass 相關(guān)方法執(zhí)行了。這是什么原因呢?

具體分析匯編才知道,isKindOfClass沒有執(zhí)行的原因是底層執(zhí)行了 objc_opt_isKindOfClass 方法。

具體分析 objc_opt_isKindOfClass

在上面的代碼中,不管上層調(diào)用的是 + isKindOfClass: 還是 - isKindOfClass:,內(nèi)部都會(huì)重定向到 objc_opt_isKindOfClass 這個(gè)方法。因?yàn)? 其本質(zhì)也是一個(gè)對(duì)象,我們稱之為 類對(duì)象。所以obj每次都有值。

分析源碼如下:

  • 如果是類:首先獲取類的元類 比較。相等則返回true。如果不相等,再獲取類的 父類 比較,相等則返回true。否則循環(huán)找 父類 比較,直到獲取到的父類為 nil,依舊沒有找到則返回false。

  • 如果是實(shí)例對(duì)象,首先獲取 比較。相等則返回true。否則和 的步驟一樣。

分析上圖代碼:

  • re2NSObject 的類,獲取 元類NSObject 不等,繼續(xù)尋找獲取 元類的父類NSObjectNSObject 相等,返回1。
  • re4ZLObject的類,獲取 元類ZLObject 不等,繼續(xù)尋找獲取 元類的父類NSObject的元類 依舊不等,繼續(xù)往上 NSObject元類的父類NSObject 依舊不等,再往上就是nil ,最后返回0。
  • re6NSObject對(duì)象,獲取 NSObject,與NSObject相等,返回1。
  • re8ZLObject對(duì)象,獲取 ZLObject,與NSObject相等,返回1。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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