iOS底層-對(duì)象的本質(zhì)及isa原理

前言

通過(guò)分析 alloc原理內(nèi)存對(duì)齊原理缕探,只是了解了如何創(chuàng)建 對(duì)象魂莫,alloc流程內(nèi)存對(duì)齊 原則,卻對(duì) 對(duì)象 的本質(zhì)及 isa 知之甚少爹耗。下面具體理解一下對(duì)象的本質(zhì)及isa的原理耙考。

基本知識(shí)

位域

  • 產(chǎn)生:有些信息在存儲(chǔ)時(shí),并不需要占用一個(gè) 完整的字節(jié)潭兽,而只需占 一個(gè)幾個(gè) 二進(jìn)制位倦始。例如在存放一個(gè)只有 01 兩種狀態(tài)時(shí), 用 一個(gè) 二進(jìn)位即可山卦⌒兀基于這種原理,C語(yǔ)言提供了一種叫做 位域 的數(shù)據(jù)結(jié)構(gòu)账蓉。

  • 定義:在定義結(jié)構(gòu)體或聯(lián)合體時(shí)枚碗,成員變量后面加 : 數(shù)字,用來(lái)限定成員變量占用的位數(shù)剔猿,這就是位域视译。

  • 目的:節(jié)省存儲(chǔ)空間,處理簡(jiǎn)便归敬。

下面通過(guò)代碼理解位域的原理酷含。

創(chuàng)建兩個(gè)結(jié)構(gòu)體,定義屬性如下汪茧,一個(gè)使用位域前結(jié)構(gòu)體椅亚,一個(gè)使用位域后結(jié)構(gòu)體。

打印其內(nèi)存結(jié)果舱污,如下:

總結(jié):

  • 沒(méi)有位域 的結(jié)構(gòu)體 s1 占用的內(nèi)存空間大小為 4 字節(jié)呀舔;使用位域 的結(jié)構(gòu)的大小為 1 字節(jié)。

  • $ p s3 可得 s3 的值:s3 = (a = 255, b = 255, c = NO, d = 255)

  • $ x/1bt &s3 可得內(nèi)存中二進(jìn)制數(shù)據(jù)為 0x00001011扩灯。低4位(從低到高)分別對(duì)應(yīng)的是a媚赖、b、c珠插、d的值惧磺;共占4個(gè)二進(jìn)制位,再進(jìn)行結(jié)構(gòu)體的內(nèi)存對(duì)齊捻撑,總大小為最大成員變量的整數(shù)倍磨隘,為 1 字節(jié)缤底;

聯(lián)合體(union)

聯(lián)合體的語(yǔ)法和結(jié)構(gòu)體非常類似,但是它們占用內(nèi)存的情況卻不同番捂。

聯(lián)合體(union)和結(jié)構(gòu)體(struct)的差異:
  • 結(jié)構(gòu)體的成員之間是 共存 的:各個(gè)成員占用不同的內(nèi)存个唧,它們互相之間沒(méi)有影響。
  • 聯(lián)合體的成員之間是 互斥 的:所有成員共用同一段內(nèi)存设预,修改一個(gè)成員的值徙歼,會(huì)影響其余所有成員。
  • 結(jié)構(gòu)體占用的內(nèi)存:大于等于 所有成員占用內(nèi)存的總和(需要內(nèi)存對(duì)齊)
  • 聯(lián)合體占用的內(nèi)存:等于最大 的成員占用的內(nèi)存絮缅,同一時(shí)刻 只能 保存一個(gè)成員的值

下面通過(guò)代碼理解聯(lián)合體(union)和結(jié)構(gòu)體(struct)的差異鲁沥。

創(chuàng)建一個(gè)聯(lián)合體

對(duì)聯(lián)合體進(jìn)行賦值,并打印其內(nèi)存情況:

1. 沒(méi)有賦值的情況:

2. a 賦值的情況:

3. b 賦值的情況:

4. c 賦值的情況:

總結(jié):

  • 聯(lián)合體可以定義多個(gè)不同類型的成員耕魄,聯(lián)合體的內(nèi)存大小由其中 最大的成員的大小 決定。

  • 聯(lián)合體中 修改 其中的某個(gè)變量會(huì) 覆蓋 其他變量的值彭谁。

  • 聯(lián)合體所有的變量 公用 一塊內(nèi)存吸奴,變量之間 互斥

  • 優(yōu)缺點(diǎn):

    • 優(yōu)點(diǎn):節(jié)省內(nèi)存缠局。

    • 缺點(diǎn):不夠包容则奥,同一時(shí)刻 只能 保存一個(gè)成員的值。

對(duì)象的本質(zhì)

如何對(duì)對(duì)象底層進(jìn)行探究狭园?首先了解一波 編譯器

準(zhǔn)備工作

  • 新建一個(gè) Project
  • 創(chuàng)建如下文件读处,并添加屬性

編譯器 Clang

  • Clang 是?個(gè)C語(yǔ)?、C++唱矛、Objective-C語(yǔ)?的 輕量級(jí)編譯器罚舱。
  • Clang 將?持其普通 lambda 表達(dá)式、返回類型的簡(jiǎn)化處理以及更好的處理 constexpr 關(guān)鍵字绎谦。
  • Clang 是?個(gè)由 Apple 主導(dǎo)編寫管闷,基于 LLVM 的C/C++/Objective-C編譯器。

把?標(biāo)?件編譯成c++?件

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

如果遇到如下 UIKit 未找到的問(wèn)題

執(zhí)行如下指令

$ clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-14.4 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk main.m

  • /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk 為本地 sdk 路徑

  • -runtime=ios-14.4:14.4為版本號(hào)窃肠,可在下面的路徑拿到包个。

結(jié)果如下:

編譯器 xcrun

xcode安裝的時(shí)候順帶安裝了xcrun命令,xcrun命令在clang的基礎(chǔ)上進(jìn)?了?些封裝冤留,要更好??些碧囊。

模擬器編譯:$ xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp

真機(jī)編譯:$ xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o os-mainarm64.cpp

模擬器編譯如下:

結(jié)果如下:

真機(jī)編譯如下:

結(jié)果如下:

main.cpp文件分析

1. 分析對(duì)象屬性

從源碼分析可得:

  • NSObject 的底層實(shí)現(xiàn)都是 objc_object 結(jié)構(gòu)體類型。

  • struct ZLObject_IMPL: 對(duì)象的底層是結(jié)構(gòu)體纤怒。

  • 嵌套 NSObject_IMPL 結(jié)構(gòu)體糯而。即 對(duì)象 繼承于 NSObject

  • 屬性以 _ 開(kāi)頭的肪跋,代表成員屬性歧蒋。

其中 NSObject_IVARS 是什么土砂?并不知道。

2. 分析 NSObject_IMPL

NSObject_IMPL 里面只有一個(gè)成員變量是 Class isa谜洽。

3. 分析 Class

main.cpp 文件中全局搜索 *Class萝映。代碼如下:

從源碼分析可得:

  • Class 的底層是 objc_class 類型的結(jié)構(gòu)體指針。

  • objc_object 里面也是只有一個(gè)成員變量是 Class isa阐虚。

  • 泛型指針id:常用的 id是一個(gè) objc_object 結(jié)構(gòu)體指針序臂,這就為什么平時(shí)在使用 id 修飾變量時(shí)為什么不加 *

  • SELSEL 也是結(jié)構(gòu)體指針实束。

4. 分析 getset 方法

從源碼分析可得:

  • 屬性的 getset 方法中通過(guò)獲取當(dāng)前對(duì)象的 首地址 + 變量的偏移值 來(lái)得到當(dāng)前變量奥秆,進(jìn)而實(shí)現(xiàn) getset 對(duì)當(dāng)前變量的修改和獲取。

  • 定義的屬性咸灿,底層自動(dòng)添加了 getset 方法构订。get 方法名為 _I_類名_屬性名set 方法名為 _I_類名_set屬性名_避矢,例如:_I_ZLObject_name_I_ZLObject_setName_悼瘾。

  • 隱含參數(shù):當(dāng)前對(duì)象 self,方法 _cmd审胸。

isa的本質(zhì)

在分析 alloc原理 時(shí)亥宿,跳過(guò)了對(duì) isa 的分析,下面具體分析 isa 的原理砂沛。

回憶 alloc 流程烫扼,其流程圖如下:

其中 obj->initInstanceIsa 方法如下:

最終都會(huì)執(zhí)行 objc_object::initIsa 方法:

這也印證了對(duì)象的底層就是 objc_object 結(jié)構(gòu)體的觀點(diǎn),即 對(duì)象 在調(diào)用 initIsa 方法時(shí)碍庵,也是底層 objc_object 的結(jié)構(gòu)體調(diào)用 initIsa方法映企。

initIsa 的流程分析

1. isa_t 分析

總結(jié):

  • isa_t 是一個(gè)聯(lián)合體,聯(lián)合體的成員變量存儲(chǔ)是 互斥 的怎抛。

  • 有兩個(gè)成員變量 bitscls卑吭,使用同一塊內(nèi)存。

2. ISA_BITFIELD 分析

其中 __has_feature(ptrauth_calls) 是什么呢马绝?

  • __has_feature:此函數(shù)的功能是判斷編譯器是否支持某個(gè)功能
  • ptrauth_calls:指針身份驗(yàn)證豆赏,針對(duì) arm64e 架構(gòu);使用 Apple A12 或更高版本 A 系列處理器的設(shè)備(如 iPhone XS富稻、iPhone XS MaxiPhone XR 或更新的設(shè)備)支持 arm64e 架構(gòu)
  • 參考鏈接:developer.apple.com
  • 驗(yàn)證流程請(qǐng)參考 jr大神掷邦。

針對(duì)這三種類型,其 64 位存儲(chǔ)分布圖如下:

遍歷分析如下:

  • nonpointer:是否對(duì) isa 指針開(kāi)啟指針優(yōu)化椭赋。0 純isa指針抚岗;1 不?是類對(duì)象地址,isa 中包含了類信息哪怔、對(duì)象的引?計(jì)數(shù)等宣蔚。
  • has_assoc:關(guān)聯(lián)對(duì)象標(biāo)志位向抢,0 沒(méi)有,1 存在
  • has_cxx_dtor:是否有 C++ 或者 Objc 的析構(gòu)器胚委,1 有析構(gòu)函數(shù)挟鸠,需要做析構(gòu)邏輯;
    0 沒(méi)有亩冬,則可以更快的釋放對(duì)象艘希。
  • shiftcls: 存儲(chǔ)類指針的值。
  • magic:?于調(diào)試器判斷當(dāng)前對(duì)象是真的對(duì)象硅急,還是沒(méi)有初始化的空間覆享。
  • weakly_referenced:是否被指向或者曾經(jīng)指向?個(gè) ARC 的弱變量,沒(méi)有弱引?的對(duì)象可以更快釋放营袜。
  • deallocating:是否正在釋放內(nèi)存撒顿。
  • has_sidetable_rc:當(dāng)對(duì)象引?技術(shù)?于 10 時(shí),則需要借?該變量存儲(chǔ)進(jìn)位荚板。
  • extra_rc:對(duì)象的引?計(jì)數(shù)值核蘸,實(shí)際上是引?計(jì)數(shù)值減 1。例如啸驯,如果對(duì)象的引?計(jì)數(shù)為 10,那么 extra_rc 為 9祟峦。如果引?計(jì)數(shù)?于 10罚斗,則需要使? has_sidetable_rc 存儲(chǔ)進(jìn)位。

3. 關(guān)聯(lián)類的方式

方式一:位運(yùn)算 (此處以 __86_64__ 為例)

類信息是存儲(chǔ)在 isa 指針中宅楞,其中 shiftcls 是對(duì)應(yīng)的對(duì)象信息针姿。按位偏移是目的是只保留shiftcls 信息,其它位的信息 清0厌衙。最終 shiftcls 的相對(duì)位置要保持不變距淫。如圖:

方式二:與運(yùn)算 isa & ISA_MASK

其中 ISA_MASK 為掩碼,用于與isa指針地址與運(yùn)算婶希。值也是區(qū)分內(nèi)核的:

  • __x86_64__ 內(nèi)核下是 0x00007ffffffffff8

  • arm64e 內(nèi)核和模擬器下是 0x007ffffffffffff8

  • arm64e 以外的其他 arm64 內(nèi)核下是 0x0000000ffffffff8

方式三:原生方法 setClass

shiftcls = (uintptr_t)newCls >> 3


補(bǔ)充:為什么類的isa和元類的地址是一樣的榕暇?

原理:isa 的結(jié)構(gòu)來(lái)看,對(duì)于對(duì)象來(lái)說(shuō)喻杈,沒(méi)有 是否釋放彤枢、引用計(jì)數(shù) 等字段,存儲(chǔ)的只有 元類 本身筒饰,所以類的 isa 和元類的地址是一樣缴啡。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市瓷们,隨后出現(xiàn)的幾起案子业栅,更是在濱河造成了極大的恐慌秒咐,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碘裕,死亡現(xiàn)場(chǎng)離奇詭異携取,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)娘汞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門歹茶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人你弦,你說(shuō)我怎么就攤上這事惊豺。” “怎么了禽作?”我有些...
    開(kāi)封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵尸昧,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我旷偿,道長(zhǎng)烹俗,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任萍程,我火速辦了婚禮幢妄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘茫负。我一直安慰自己蕉鸳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布忍法。 她就那樣靜靜地躺著潮尝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪饿序。 梳的紋絲不亂的頭發(fā)上勉失,一...
    開(kāi)封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音原探,去河邊找鬼乱凿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛踢匣,可吹牛的內(nèi)容都是我干的告匠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼离唬,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼后专!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起输莺,我...
    開(kāi)封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤戚哎,失蹤者是張志新(化名)和其女友劉穎裸诽,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體型凳,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丈冬,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了甘畅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片埂蕊。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖疏唾,靈堂內(nèi)的尸體忽然破棺而出蓄氧,到底是詐尸還是另有隱情,我是刑警寧澤槐脏,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布喉童,位于F島的核電站,受9級(jí)特大地震影響顿天,放射性物質(zhì)發(fā)生泄漏堂氯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一牌废、第九天 我趴在偏房一處隱蔽的房頂上張望咽白。 院中可真熱鬧,春花似錦鸟缕、人聲如沸局扶。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至畜埋,卻和暖如春莫绣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背悠鞍。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工对室, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人咖祭。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓掩宜,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親么翰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子牺汤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容