Runtime(2)--從runtime中看對象

運(yùn)行時源碼版本 objc4-750.1

OC中的id(實例對象)以及NSObject(類)到底是什么

//在objc-private.h文件中
typedef struct objc_class *Class;
typedef struct objc_object *id;
//在objc-private.h文件中
struct objc_object {
private:
    isa_t isa; //共用體類型, 具體的定義在上面

public:
    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();

    // getIsa() allows this to be a tagged pointer object
    Class getIsa();
    *** 省略 ***
};
//在objc-runtime-new.h文件中
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();
    }
   *** 省略 ***
};

通過代碼可以看出平時我們的實例對象, 類都是都是C語言結(jié)構(gòu)體, 并且objc_class是繼承與objc_object. 也就是說其實類也某一個 '類' 的實例對象, 這個特殊的 '類' 我們稱之為 元類

元類(meta class)

通過上面的代碼我們可以確定Objective-C 中類也是一個對象, 我們把類對象所屬的類型稱之為元類(meta class), 元類(meta class)也是一個叫做根元類(root meta class)的實例,那么有了元類的好處是啥呢?
我們知道實例對象的方法是定義在它所屬的類當(dāng)中的,那么類方法自然是定義在元類當(dāng)中的. 通過這個元類可以保證無論是類還是對象都能通過相同的機(jī)制查找方法的實現(xiàn)待逞。


從圖中可以看出來對象的isa指向它所屬的類,元類的isa都指向根元類(root meta class), 而根元類的isa則指向了自己, 這樣就形成了一個閉環(huán)的結(jié)構(gòu).

當(dāng)實例方法被調(diào)用時甥角,它可以通過持有的 isa 來查找對應(yīng)的類,然后在這里的 class_data_bits_t 結(jié)構(gòu)體中查找對應(yīng)方法的實現(xiàn)识樱。同時嗤无,每一個 objc_class 也有一個指向自己的父類的指針 super_class 用來查找繼承的方法震束。

class_data_bits_t 中存有 Class 的對應(yīng)方法,具體如何存儲及查找以后會另做分析

isa_t isa(共用體類型的isa)

從上面objc_object的定義以及objc_class是繼承與objc_object的關(guān)系我們可以得到如下圖的關(guān)系


所有繼承自 NSObject 的類實例化后的對象都會包含一個類型為 isa_t 的共用體.

那么 isa 到底是什么呢当犯?其實在 ARM 64 之前垢村,isa 直接保存了類對象或者元類對象的地址,而之后使用了位域結(jié)構(gòu)存儲了更多信息嚎卫。

//64位之后的isa定義
//在objc-private.h文件中
union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

看一下ISA_BITFIELD定義

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
#   define ISA_BITFIELD                                                      \
      uintptr_t nonpointer        : 1;                                       \
      uintptr_t has_assoc         : 1;                                       \
      uintptr_t has_cxx_dtor      : 1;                                       \
      uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
      uintptr_t magic             : 6;                                       \
      uintptr_t weakly_referenced : 1;                                       \
      uintptr_t deallocating      : 1;                                       \
      uintptr_t has_sidetable_rc  : 1;                                       \
      uintptr_t extra_rc          : 19
#   define RC_ONE   (1ULL<<45)
#   define RC_HALF  (1ULL<<18)

# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
#   define ISA_BITFIELD                                                        \
      uintptr_t nonpointer        : 1;                                         \
      uintptr_t has_assoc         : 1;                                         \
      uintptr_t has_cxx_dtor      : 1;                                         \
      uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
      uintptr_t magic             : 6;                                         \
      uintptr_t weakly_referenced : 1;                                         \
      uintptr_t deallocating      : 1;                                         \
      uintptr_t has_sidetable_rc  : 1;                                         \
      uintptr_t extra_rc          : 8
#   define RC_ONE   (1ULL<<56)
#   define RC_HALF  (1ULL<<7)

# else
#   error unknown architecture for packed isa
# endif

上面列出了ISA_BITFIELD在arm64架構(gòu)和x86_64架構(gòu)下的定義, iOS 應(yīng)用為 arm64 架構(gòu)環(huán)境拓诸。由于我的測試代碼是基于 macOS 的所以我們下面分析的時候看的是 x86_64 架構(gòu)下的定義, 這兩種結(jié)構(gòu)的實現(xiàn)和位數(shù)可能有些差別,但這些字段都是存在的.所以不會影響我們對isa的理解.

// __x86_64__架構(gòu)
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
#   define ISA_BITFIELD                                                        \
      uintptr_t nonpointer        : 1;                                         \
      uintptr_t has_assoc         : 1;                                         \
      uintptr_t has_cxx_dtor      : 1;                                         \
      uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
      uintptr_t magic             : 6;                                         \
      uintptr_t weakly_referenced : 1;                                         \
      uintptr_t deallocating      : 1;                                         \
      uintptr_t has_sidetable_rc  : 1;                                         \
      uintptr_t extra_rc          : 8
#   define RC_ONE   (1ULL<<56)
#   define RC_HALF  (1ULL<<7)

先看一下每一個參數(shù)的含義

變量名 所占位數(shù) 含義
nonpointer 1 0 表示普通的 isa 指針,1 表示使用優(yōu)化倍谜,存儲引用計數(shù)
has_assoc 1 表示該對象是否包含 associated object尔崔,如果沒有,則析構(gòu)時會更快
has_cxx_dtor 1 表示該對象是否有 C++ 或 ARC 的析構(gòu)函數(shù)烙常,如果沒有鹤盒,則析構(gòu)時更快
shiftcls 44 類的指針
magic 6 固定值侦锯,用于在調(diào)試時分辨對象是否未完成初始化
weakly_referenced 1 表示該對象是否有過 weak 對象尺碰,如果沒有亲桥,則析構(gòu)時更快
deallocating 1 表示該對象是否正在析構(gòu)
has_sidetable_rc 1 表示該對象的引用計數(shù)值是否過大無法存儲在 isa 指針
extra_rc 8 存儲引用計數(shù)值減一后的結(jié)果

isa是一個共用體,共用體的所有成員占用同一段內(nèi)存.而isa總共占用的內(nèi)存是64位, 表中的數(shù)字代表每個變量占用的位數(shù).

cache_t cache(方法緩存)

struct cache_t {
    struct bucket_t *_buckets;  
    mask_t _mask;               
    mask_t _occupied;         
};

#if __LP64__
typedef uint32_t mask_t;  // x86_64 & arm64 asm are less efficient with 16-bits
#else
typedef uint16_t mask_t;
#endif
typedef uintptr_t cache_key_t;

通過源碼我們可以知道cache_t是有一個bucket_t類型的結(jié)構(gòu)體和兩個uint32_t類型的變量組成题篷。

  • bucket_t是一個散列表番枚,用來存儲類中的方法的鏈表,緩存曾經(jīng)調(diào)用過的方法葫笼,可以調(diào)高方法的查找速度
  • _mask是表示分配用來緩存buckets的總數(shù)
  • _occupied是表明當(dāng)前實際占用緩存buckets的個數(shù)

其實cache主要的作用就是為了提高調(diào)用方法的效率提升性能路星。

  • 沒有cache機(jī)制:當(dāng)對象調(diào)用方法的時候,先根據(jù)對象的isa指針去它對應(yīng)的類中尋找方法呈昔,然后在類的methodLists中尋找垫挨,如果沒有找到九榔,則通過super_class指針到父類中的methodList里面去找,一旦找到方法就調(diào)用剩蟀,沒有找到的話就進(jìn)行消息轉(zhuǎn)發(fā)切威,或者報出異常。(效率很低)
  • 有cache機(jī)制:當(dāng)對象調(diào)用方法的時候先朦,先去cache中尋找方法喳魏,如果沒有找到,再依照上述方法到methodLists中查找迷郑。

<<<<<<<<<<<<<< 后續(xù)持續(xù)更新 >>>>>>>>>>>>>>>>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嗡害,一起剝皮案震驚了整個濱河市霸妹,隨后出現(xiàn)的幾起案子念搬,更是在濱河造成了極大的恐慌摆出,老刑警劉巖偎漫,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件象踊,死亡現(xiàn)場離奇詭異棚壁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)史隆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門泌射,熙熙樓的掌柜王于貴愁眉苦臉地迎上來熔酷,“玉大人豺裆,你說我怎么就攤上這事臭猜。” “怎么了阴颖?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長帅矗。 經(jīng)常有香客問我煞烫,道長,這世上最難降的妖魔是什么凛俱? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任蒲犬,我火速辦了婚禮,結(jié)果婚禮上赫编,老公的妹妹穿的比我還像新娘擂送。我一直安慰自己唯欣,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布蟀拷。 她就那樣靜靜地躺著匹厘,像睡著了一般愈诚。 火紅的嫁衣襯著肌膚如雪牛隅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機(jī)與錄音默伍,去河邊找鬼也糊。 笑死,一個胖子當(dāng)著我的面吹牛掐隐,可吹牛的內(nèi)容都是我干的钞馁。 我是一名探鬼主播匿刮,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼熟丸,長吁一口氣:“原來是場噩夢啊……” “哼虑啤!你這毒婦竟也來了狞山?” 一聲冷哼從身側(cè)響起叉寂,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤屏鳍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后驳遵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體堤结,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鸭丛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年瘾带,在試婚紗的時候發(fā)現(xiàn)自己被綠了熟菲。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖贞绵,靈堂內(nèi)的尸體忽然破棺而出榨崩,到底是詐尸還是另有隱情,我是刑警寧澤翩剪,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布前弯,位于F島的核電站恕出,受9級特大地震影響违帆,放射性物質(zhì)發(fā)生泄漏刷后。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一丧裁、第九天 我趴在偏房一處隱蔽的房頂上張望煎娇。 院中可真熱鬧逊桦,春花似錦抑进、人聲如沸寺渗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽玲躯。三九已至跷车,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間善玫,已是汗流浹背茅郎。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工系冗, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留劳坑,地道東北人距芬。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓框仔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親银舱。 傳聞我的和親對象是個殘疾皇子寻馏,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

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