日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

iOS-底层原理 06: cls 与类的关联原理

發(fā)布時間:2023/12/16 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 iOS-底层原理 06: cls 与类的关联原理 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

聯(lián)合體(union)

?

構(gòu)造數(shù)據(jù)類型的方式有以下兩種:

  • 結(jié)構(gòu)體(struct)
  • 聯(lián)合體(union,也稱為共用體)

結(jié)構(gòu)體

結(jié)構(gòu)體是指把不同的數(shù)據(jù)組合成一個整體,其變量是共存的,變量不管是否使用,都會分配內(nèi)存。

  • 缺點:所有屬性都分配內(nèi)存,比較浪費內(nèi)存,假設(shè)有4個int成員,一共分配了16字節(jié)的內(nèi)存,但是在使用時,你只使用了4字節(jié),剩余的12字節(jié)就是屬于內(nèi)存的浪費

  • 優(yōu)點:存儲容量較大,包容性強,且成員之間不會相互影響

聯(lián)合體

聯(lián)合體也是由不同的數(shù)據(jù)類型組成,但其變量是互斥的,所有的成員共占一段內(nèi)存。而且共用體采用了內(nèi)存覆蓋技術(shù),同一時刻只能保存一個成員的值,如果對新的成員賦值,就會將原來成員的值覆蓋掉

  • 缺點:,包容性弱

  • 優(yōu)點:所有成員共用一段內(nèi)存,使內(nèi)存的使用更為精細(xì)靈活,同時也節(jié)省了內(nèi)存空間

兩者的區(qū)別

  • 內(nèi)存占用情況

    • 結(jié)構(gòu)體的各個成員會占用不同的內(nèi)存,互相之間沒有影響
    • 共用體的所有成員占用同一段內(nèi)存,修改一個成員會影響其余所有成員
  • 內(nèi)存分配大小

    • 結(jié)構(gòu)體內(nèi)存 >= 所有成員占用的內(nèi)存總和(成員之間可能會有縫隙)
    • 共用體占用的內(nèi)存等于最大的成員占用的內(nèi)存

isa的類型 isa_t

以下是isa指針的類型isa_t的定義,從定義中可以看出是通過聯(lián)合體(union)定義的。

union isa_t { //聯(lián)合體isa_t() { }isa_t(uintptr_t value) : bits(value) { }//提供了cls 和 bits ,兩者是互斥關(guān)系Class cls;uintptr_t bits; #if defined(ISA_BITFIELD)struct {ISA_BITFIELD; // defined in isa.h}; #endif };

isa_t類型使用聯(lián)合體的原因也是基于內(nèi)存優(yōu)化的考慮,這里的內(nèi)存優(yōu)化是指在isa指針中通過char + 位域(即二進(jìn)制中每一位均可表示不同的信息)的原理實現(xiàn)。通常來說,isa指針占用的內(nèi)存大小是8字節(jié),即64位,已經(jīng)足夠存儲很多的信息了,這樣可以極大的節(jié)省內(nèi)存,以提高性能

從isa_t的定義中可以看出:

  • 提供了兩個成員,cls 和 bits,由聯(lián)合體的定義所知,這兩個成員是互斥的,也就意味著,當(dāng)初始化isa指針時,有兩種初始化方式

    • 通過cls初始化,bits無默認(rèn)值

    • 通過bits初始化,cls有默認(rèn)值

  • 還提供了一個結(jié)構(gòu)體定義的位域,用于存儲類信息及其他信息,結(jié)構(gòu)體的成員ISA_BITFIELD,這是一個宏定義,有兩個版本 __arm64__(對應(yīng)ios 移動端) 和 __x86_64__(對應(yīng)macOS),以下是它們的一些宏定義,如下圖所示

  • 位域的宏定義

    • nonpointer有兩個值,表示自定義的類等,占1位

      • 0:純isa指針
      • 1:不只是類對象地址,isa中包含了類信息、對象的引用計數(shù)等
    • has_assoc表示關(guān)聯(lián)對象標(biāo)志位,占1位

      • 0:沒有關(guān)聯(lián)對象
      • 1:存在關(guān)聯(lián)對象
    • has_cxx_dtor 表示該對象是否有C++/OC的析構(gòu)器(類似于dealloc),占1位

      • 如果有析構(gòu)函數(shù),則需要做析構(gòu)邏輯
      • 如果沒有,則可以更快的釋放對象
    • shiftcls表示存儲類的指針的值(類的地址), 即類信息

      • arm64中占 33位,開啟指針優(yōu)化的情況下,在arm64架構(gòu)中有33位用來存儲類指針
      • x86_64中占 44位
    • magic 用于調(diào)試器判斷當(dāng)前對象是真的對象 還是 沒有初始化的空間,占6位

    • weakly_refrenced是 指對象是否被指向 或者 曾經(jīng)指向一個ARC的弱變量

      • 沒有弱引用的對象可以更快釋放
    • deallocating 標(biāo)志對象是是否正在釋放內(nèi)存

    • has_sidetable_rc表示 當(dāng)對象引用計數(shù)大于10時,則需要借用該變量存儲進(jìn)位

    • extra_rc(額外的引用計數(shù)) ,表示該對象的引用計數(shù)值,實際上是引用計數(shù)值減1

      • 如果對象的引用計數(shù)為10,那么extra_rc為9(這個僅為舉例說明),實際上iPhone 真機上的 extra_rc 是使用 19位來存儲引用計數(shù)的

針對兩種不同平臺,其isa的存儲情況如圖所示

原理探索

  • 通過alloc --> _objc_rootAlloc --> callAlloc --> _objc_rootAllocWithZone --> _class_createInstanceFromZone方法路徑,查找到initInstanceIsa,并進(jìn)入其原理實現(xiàn)
inline void objc_object::initInstanceIsa(Class cls, bool hasCxxDtor) {ASSERT(!cls->instancesRequireRawIsa());ASSERT(hasCxxDtor == cls->hasCxxDtor());//初始化isainitIsa(cls, true, hasCxxDtor); }
  • 進(jìn)入initIsa方法的源碼實現(xiàn),主要是初始化isa指針
  • 該方法的邏輯主要分為兩部分
    • 通過?cls?初始化?isa
    • 通過?bits?初始化?isa

驗證 isa指針 位域(0-64)

根據(jù)前文提及的0-64位域,可以在這里通過initIsa方法中證明有isa指針中有這些位域(目前是處于macOS,所以使用的是x86_64)

  • 首先通過main中的LGPerson 斷點 --> initInstanceIsa --> initIsa --> 走到else中的 isa初始化

  • 執(zhí)行l(wèi)ldb命令:p newisa,得到newisa的詳細(xì)信息

  • 繼續(xù)往下執(zhí)行,走到newisa.bits = ISA_MAGIC_VALUE;下一行,表示為isa的bits成員賦值,重新執(zhí)行l(wèi)ldb命令p newisa,得到的結(jié)果如下


    通過與前一個newsize的信息對比,發(fā)現(xiàn)isa指針中有一些變化,如下圖所示

    • 其中magic是59是由于將isa指針地址轉(zhuǎn)換為二進(jìn)制,從47(因為前面有4個位域,共占用47位,地址是從0開始)位開始讀取6位,再轉(zhuǎn)換為十進(jìn)制,如下圖所示

isa 與 類 的關(guān)聯(lián)

cls 與 isa 關(guān)聯(lián)原理就是isa指針中的shiftcls位域中存儲了類信息,其中initInstanceIsa的過程是將 calloc 指針 和當(dāng)前的 類cls 關(guān)聯(lián)起來,有以下幾種驗證方式:

  • 【方式一】通過initIsa方法中的newisa.shiftcls = (uintptr_t)cls >> 3;驗證

  • 【方式二】通過isa指針地址與ISA_MSAK 的值 & 來驗證

  • 【方式三】通過runtime的方法object_getClass驗證

  • 【方式四】通過位運算驗證

方式一:通過 initIsa 方法

  • 運行至newisa.shiftcls = (uintptr_t)cls >> 3;前一步,其中 shiftcls存儲當(dāng)前類的值信息

    • 此時查看cls,是LGPerson類

    • shiftcls賦值的邏輯是將 LGPerson進(jìn)行編碼后,右移3位

  • 執(zhí)行l(wèi)ldb命令p (uintptr_t)cls,結(jié)果為(uintptr_t) $2 = 4294975720,再右移三位,有以下兩種方式(任選其一),將得到536871965存儲到newisa的shiftcls中

    • p (uintptr_t)cls >> 3

    • 通過上一步的結(jié)果$2,執(zhí)行l(wèi)ldb命令p $2 >> 3

  • 繼續(xù)執(zhí)行程序到isa = newisa;部分,此時執(zhí)行p newisa


    與bits賦值結(jié)果的對比,bits的位域中有兩處變化

    • cls 由默認(rèn)值,變成了LGPerson,將isa與cls完美關(guān)聯(lián)
    • shiftcls由0變成了536871965

所以isa中通過初始化后的成員的值變化過程,如下圖所示

為什么在shiftcls賦值時需要類型強轉(zhuǎn)?

因為內(nèi)存的存儲不能存儲字符串,機器碼只能識別 0 、1這兩種數(shù)字,所以需要將其轉(zhuǎn)換為uintptr_t數(shù)據(jù)類型,這樣shiftcls中存儲的類信息才能被機器碼理解, 其中uintptr_t是long

為什么需要右移3位?

主要是由于shiftcls處于isa指針地址的中間部分,前面還有3個位域,為了不影響前面的3個位域的數(shù)據(jù),需要右移將其抹零。

方式二:通過 isa & ISA_MSAK

  • 在方式一后,繼續(xù)執(zhí)行,回到_class_createInstanceFromZone方法,此時cls 與 isa已經(jīng)關(guān)聯(lián)完成,執(zhí)行po objc

  • 執(zhí)行x/4gx obj,得到isa指針的地址0x001d8001000020e9

  • 將isa指針地址 & ISA_MASK (處于macOS,使用x86_64中的宏定義),即 po 0x001d8001000020e9 & 0x00007ffffffffff8 ,得出LGPerson

    • arm64中,ISA_MASK 宏定義的值為0x0000000ffffffff8ULL

    • x86_64中,ISA_MASK 宏定義的值為0x00007ffffffffff8ULL

方式三:通過 object_getClass

通過查看object_getClass的源碼實現(xiàn),同樣可以驗證isa與類關(guān)聯(lián)的原理,有以下幾步:

  • main中導(dǎo)入#import <objc/runtime.h>

  • 通過runtime的api,即object_getClass函數(shù)獲取類信息

object_getClass(<#id _Nullable obj#>)
  • 查看object_getClass函數(shù) 源碼的實現(xiàn)

  • 點擊進(jìn)入object_getClass 底層實現(xiàn)

  • 進(jìn)入getIsa的源碼實現(xiàn)

  • 點擊ISA(),進(jìn)入源碼,可以看到如果是indexed類型,執(zhí)行if流程,反之 執(zhí)行的是else流程

    • 在else流程中,拿到isa的bits這個位,再 & ISA_MASK,這與方式二中的原理是一致的,獲得當(dāng)前的類信息
    • 從這里也可以得出 cls 與 isa 已經(jīng)完美關(guān)聯(lián)

方式四:通過位運算

  • 回到_class_createInstanceFromZone方法。通過x/4gx obj 得到obj的存儲信息,當(dāng)前類的信息存儲在isa指針中,且isa中的shiftcls此時占44位(因為處于macOS環(huán)境)

  • 想要讀取中間的44位 類信息,就需要經(jīng)過位運算 ,將右邊3位,和左邊除去44位以外的部分都抹零,其相對位置是不變的。其位運算過程如圖所示,其中shiftcls即為需要讀取的類信息

    • 將isa地址右移3位:p/x 0x001d8001000020e9 >> 3 ,得到0x0003b0002000041d

    • 在將得到的0x0003b0002000041d``左移20位:p/x 0x0003b0002000041d << 20 ,得到0x0002000041d00000

      • 為什么是左移20位?因為先右移了3位,相當(dāng)于向右偏移了3位,而左邊需要抹零的位數(shù)有17位,所以一共需要移動20位
    • 將得到的0x0002000041d00000 再右移17位:p/x 0x0002000041d00000 >> 17 得到新的0x00000001000020e8

  • 獲取cls的地址 與 上面的進(jìn)行驗證 :p/x cls 也得出0x00000001000020e8,所以由此可以證明 cls 與 isa 是關(guān)聯(lián)的


?

總結(jié)

以上是生活随笔為你收集整理的iOS-底层原理 06: cls 与类的关联原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 好吊视频一区二区三区 | 999精彩视频 | 人人爱爱 | 动漫一区二区三区 | 国产精品99re | 日韩有码在线视频 | 久久白浆 | 三级网站国产 | 欧美成人国产精品高潮 | 成人精品视频在线播放 | 免费在线毛片 | 少妇高潮一区二区三区69 | 美国一级特黄 | 538国产精品视频一区二区 | 天堂va蜜桃一区二区三区 | 秋霞午夜影院 | 最近中文字幕在线视频 | 免费看日产一区二区三区 | chinese中国性按摩hd | 欧美一区二区三区免费在线观看 | av毛片在线免费看 | 欧美14sex性hd摘花 | 国产伦精品一区二区三区四区 | 毛片毛片毛片毛片毛片毛片毛片毛片毛片毛片 | 国产人成视频在线观看 | 成人国产片女人爽到高潮 | 黄色污小说 | 蜜桃臀一区二区三区 | 欧美乱仑 | 来吧亚洲综合网 | 麻豆精品av | 做视频 | 天天干干干 | 成人人伦一区二区三区 | 亚洲性影院 | 欧美一区二区久久 | 永久免费看黄网站 | 一区二区高潮 | 在线观看成人 | 国产视频四区 | 深夜视频在线观看 | www.中文字幕在线观看 | 中文字幕在线观看 | 国产黄在线观看 | 人妻互换一二三区激情视频 | 久久精工是国产品牌吗 | 欧美性爱视频久久 | 一道本在线视频 | 中文字幕乱妇无码av在线 | 五月天av在线| 亚洲九九 | 久久av一区二区三 | av最新地址 | 色999在线 | 一本色道久久88加勒比—综合 | 夜夜爽影院 | 免费a级片视频 | 欧美 日韩 国产 一区 | 中文在线一区二区 | 美女国产一区 | 亚洲福利影院 | 中文字幕人妻一区二区三区视频 | 高清国产一区二区 | 天天玩天天操 | 伊人av综合网 | 国产又黄又骚 | 午夜在线观看av | 波多野结衣三区 | 涩色视频| 亚洲成人福利 | 91在线视频导航 | 男人操女人的免费视频 | 国产成人无码精品久久久久 | 久久久久久网 | 国产男女av | 亚洲av片在线观看 | 天天狠狠 | jizzzxxxx| 日韩久久久久久久久久 | 成人精品久久久 | 日本亚洲欧美在线 | 开心激情网站 | 欧美性极品xxxx做受 | 日韩在线一区二区 | 女女互磨互喷水高潮les呻吟 | 91免费版视频 | 亚洲88av | 国产在线黄色 | 精品日本一区二区三区 | 日本三级吹潮 | 中文一区视频 | 中文字幕高清在线 | 日韩欧美久久 | 黄色在线观看免费 | 99色 | 欧美日韩国产综合在线 | 在线免费小电影 | 婷婷视频在线观看 | 亚洲一区二区三区在线 |