Objective-C 類的本質(zhì)

Objective-C (以下簡稱 OC )是一門動態(tài)性強(qiáng)的編程語言泊藕,OC 的動態(tài)性是基于 Runtime 來實現(xiàn)的,Runtime 系統(tǒng)是由 C\C++\匯編語言 編寫的虐译,提供的 API 基本都是 C 語言的忿薇。這里我們從蘋果提供的 Runtime 代碼來探究類的本質(zhì)毕骡。

runtime 源碼地址

legacy 版本

OCruntime 分為兩個版本.一個是 legacy 版本,一個是 modern 版本。相信很多讀者都見過下面這段代表 OC 類結(jié)構(gòu)的代碼:

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

其實這段代碼就是 legacy 版本 已經(jīng)在 2006 年的 WWDC 大會上發(fā)布 Objective-C 2.0 后棄用了奏候, OBJC2_UNAVAILABLE 標(biāo)記的內(nèi)容已經(jīng)不再使用循集,那么現(xiàn)在的結(jié)構(gòu)是什么呢?

對象

OC 中蔗草,每一個對象都是類的實例咒彤,先直接來看源碼中的結(jié)構(gòu):

struct objc_object {
private:
    isa_t isa;
    // ...
}

代表對象的結(jié)構(gòu)中只有一個 isa 的成員變量疆柔,在 arm64 架構(gòu)下,系統(tǒng)對 isa 進(jìn)行了優(yōu)化镶柱,它不光存著地址信息旷档,還存著其他信息。因此對象的本質(zhì)就是包含了一個私有成員變量 isa 的結(jié)構(gòu)體奸例,而 isa 存著的地址就指向著對象所屬的類彬犯。不同的對象有不同的成員變量,編譯后查吊,每個對象的結(jié)構(gòu)體也會存著自己的成員變量的谐区。

使用命令獲取編譯后的代碼 -sdk iphoneos clang -arch arm64 -rewrite-objc Coder.m

@interface Coder : Person
@property (nonatomic, copy) NSString *name;
@end
    
// 編譯后查看 `Coder` 的實現(xiàn)
struct NSObject_IMPL {
    Class isa;
};

struct Coder_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    NSString * _Nonnull _name;
};

之所以成員變量的值存在對象中,這個也很好理解逻卖,每個對象肯定是獨(dú)立存在的宋列,都需要擁有自己的變量值。而變量名稱和方法等等存在什么地方呢评也,就是類了炼杖!

類存著成員變量的類型,方法等等盗迟,源碼如下:

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() { 
        return bits.data();
    }
    // 省略...
}

首先可以看到的一點(diǎn)是 objc_class 繼承了 objc_object坤邪,因此其實 OC 中的類也可以理解為一種對象,稱之為類對象罚缕,在 legacy 版本中艇纺,對象的結(jié)構(gòu)體中只有一個 isa 指針,指向它的類對象邮弹,而類對象中也有一個 isa 指針黔衡,指向它的元類。modern 版本使用繼承后腌乡,類對象的結(jié)構(gòu)體就繼承了這個優(yōu)化后的 isa 變量盟劫。但對比兩個版本,會發(fā)現(xiàn) modern 版本中除了superclass&cache 与纽,其余的很多變量不在了侣签,并多了一個 bits 變量。

struct class_data_bits_t {
    uintptr_t bits;
    
    class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
    // ...
}

這個結(jié)構(gòu)體里面是通過一個位運(yùn)算獲取的指向 class_rw_t 的指針渣锦,可見 bits 存著 class_rw_t 結(jié)構(gòu)體的指針和一些其他信息硝岗。然后把目光轉(zhuǎn)到 class_rw_t 上:

'rw' 和 ro' 分別表示 'readwrite' 和 'readonly'

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;
    // ...
}

可以看到原先 legacy 版本中的方法、屬性和協(xié)議列表就存在這個里面袋毙,這幾個列表可以理解為是二維數(shù)組型檀,是可讀可寫的,包含了類的初始內(nèi)容听盖、分類的內(nèi)容胀溺,二維數(shù)組方便增加裂七。 而這里又有一個 class_ro_t :

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
    // ....
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;
    // ....
};

class_ro_t 里面的 baseMethodList、baseProtocols仓坞、ivars背零、baseProperties 可以理解為是一維數(shù)組,是只讀的无埃,包含了類的初始內(nèi)容徙瓶。

從這里我們也能看出分類不能動態(tài)添加成員變量到類對象的原因,分類是通過 runtime 加載的嫉称,這時候類結(jié)構(gòu)已經(jīng)確定下來了侦镇,并且這里保存成員變量的內(nèi)存是只讀的。

元類

上面已經(jīng)提到织阅,類對象的 isa 中儲存的地址指向的就算類對象的類壳繁,稱之為元類,元類儲存著對象方法荔棉。也就是說實例方法是儲存在類中的闹炉,類方法是存儲在元類中的。用一個經(jīng)典的圖來表示對象润樱、類和元類的關(guān)系渣触。

object_model.png

圖中已經(jīng)很好的闡述了三者之間的關(guān)系,不過這里需要強(qiáng)調(diào)兩點(diǎn)壹若。

  • 元類的 isa 指向的是基類的元類昵观。
  • 基類的元類的 superclass 指向的是基類

這兩個點(diǎn)很容易被忽略,在一些面試題中經(jīng)常出現(xiàn)舌稀。

參考

Objective-C 對象模型

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市灼擂,隨后出現(xiàn)的幾起案子壁查,更是在濱河造成了極大的恐慌,老刑警劉巖剔应,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件睡腿,死亡現(xiàn)場離奇詭異,居然都是意外死亡峻贮,警方通過查閱死者的電腦和手機(jī)席怪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纤控,“玉大人挂捻,你說我怎么就攤上這事〈颍” “怎么了刻撒?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵骨田,是天一觀的道長。 經(jīng)常有香客問我声怔,道長态贤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任醋火,我火速辦了婚禮悠汽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘芥驳。我一直安慰自己柿冲,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布晚树。 她就那樣靜靜地躺著姻采,像睡著了一般。 火紅的嫁衣襯著肌膚如雪爵憎。 梳的紋絲不亂的頭發(fā)上慨亲,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機(jī)與錄音宝鼓,去河邊找鬼刑棵。 笑死,一個胖子當(dāng)著我的面吹牛愚铡,可吹牛的內(nèi)容都是我干的蛉签。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼沥寥,長吁一口氣:“原來是場噩夢啊……” “哼碍舍!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起邑雅,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤片橡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后淮野,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捧书,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年骤星,在試婚紗的時候發(fā)現(xiàn)自己被綠了经瓷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡洞难,死狀恐怖舆吮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤歪泳,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布萝勤,位于F島的核電站,受9級特大地震影響呐伞,放射性物質(zhì)發(fā)生泄漏敌卓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一伶氢、第九天 我趴在偏房一處隱蔽的房頂上張望趟径。 院中可真熱鬧,春花似錦癣防、人聲如沸蜗巧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽幕屹。三九已至,卻和暖如春级遭,著一層夾襖步出監(jiān)牢的瞬間望拖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工挫鸽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留说敏,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓丢郊,卻偏偏與公主長得像盔沫,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子枫匾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

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