iOS底層-類結(jié)構(gòu)分析

1悟泵、什么是類

iOS中所以的類繼承NSObject猪叙,那么在底層NSObject的結(jié)構(gòu)是怎么樣的呢浅碾?
使用clang編譯一下如下代碼

#import <Foundation/Foundation.h>
@interface MCPerson : NSObject
@property (nonatomic,copy) NSString *name;
@end
@implementation MCPerson
@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MCPerson *p = [MCPerson alloc];
        p.name = @"Hello";
    }
    return 0;
}

編譯命令

clang -rewrite-objc main.m

生成文件main.cpp,搜索找到相關(guān)代碼

struct objc_object {
    Class _Nonnull isa __attribute__((deprecated));
};
struct objc_class : objc_object {...};//在objc-runtime-new源碼中
typedef struct objc_object *id;//在objc-runtime-new源碼中
typedef struct objc_class *Class;//結(jié)構(gòu)體指針
typedef struct objc_object NSObject;//NSObject在底層是一個(gè)結(jié)構(gòu)體里面包含isa指針

剛好證明OC中的萬(wàn)物皆對(duì)象原則影暴,所以派生類都繼承NSObject,在底層都繼承objc_object結(jié)構(gòu)體腻豌,簡(jiǎn)單來(lái)說(shuō)家坎,類的底層是繼承objc_object,包含isa吝梅、superclass虱疏、cache、bits的結(jié)構(gòu)體

2苏携、類的結(jié)構(gòu)

objc_class源碼

struct objc_class : objc_object {
    // Class ISA;                 //繼承objc_object做瞪,所以有isa指針
    Class superclass;       //父類
    cache_t cache;             // 主要是緩存方法
    class_data_bits_t bits;    // 數(shù)據(jù)存儲(chǔ)
    class_rw_t *data() const {
        return bits.data();
    }
}
  • Class isa 指針
    在對(duì)象初始化的時(shí)候通過(guò)isa將對(duì)象和類關(guān)聯(lián)起來(lái),而類也是一個(gè)對(duì)象右冻,俗稱類對(duì)象装蓬,類對(duì)象也是需要和元類關(guān)聯(lián)起來(lái)的著拭,所以類中也有isa指針。


    isa走位圖
  • Class superclass
    這個(gè)很好理解矛物,就是父類茫死,一般來(lái)說(shuō),大部分類的父類都是NSObject履羞,根元類的父類也是NSObject峦萎,NSObject的父類是nil。

  • cache_t cache

struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
    explicit_atomic<struct bucket_t *> _buckets;
    explicit_atomic<mask_t> _mask;
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    explicit_atomic<uintptr_t> _maskAndBuckets;
    mask_t _mask_unused;
    ...
};

感覺(jué)這個(gè)成員變量的作用主要是用來(lái)緩存方法忆首,留個(gè)疑問(wèn)爱榔。

  • class_data_bits_t bits
struct class_data_bits_t {
    friend objc_class;
    // Values are the FAST_ flags above.
    uintptr_t bits;
    class_rw_t* data() const {//核心,存儲(chǔ)方法糙及、屬性详幽、協(xié)議,可以動(dòng)態(tài)修改
         return (class_rw_t *)(bits & FAST_DATA_MASK);
    }

    const class_ro_t *safe_ro() {//存儲(chǔ)成員變量浸锨,不可動(dòng)態(tài)修改唇聘,類似readonly
        class_rw_t *maybe_rw = data();
        if (maybe_rw->flags & RW_REALIZED) {
            // maybe_rw is rw
            return maybe_rw->ro();
        } else {
            // maybe_rw is actually ro
            return (class_ro_t *)maybe_rw;
        }
    }
void setData(class_rw_t *newData) {
    bits.setData(newData);
}

bool hasCustomRR() const {
    return !bits.getBit(FAST_HAS_DEFAULT_RR);
}

void setHasDefaultRR() {
    bits.setBits(FAST_HAS_DEFAULT_RR);
}

void setHasCustomRR() {
    bits.clearBits(FAST_HAS_DEFAULT_RR);
}
    ...
};

這里值得我們注意的是,objc_class 的 data()方法其實(shí)是返回的 bits 的 data() 方法柱搜,而通過(guò)這個(gè) data() 方法迟郎,我們發(fā)現(xiàn)諸如類的字節(jié)對(duì)齊、ARC聪蘸、元類等特性都有 data() 的出現(xiàn)宪肖,這間接說(shuō)明 bits 屬性其實(shí)是個(gè)大容器,有關(guān)于內(nèi)存管理健爬、C++ 析構(gòu)等內(nèi)容在其中有定義控乾。
重點(diǎn):
class_rw_t* data() //存儲(chǔ)方法、屬性娜遵、協(xié)議蜕衡,可以動(dòng)態(tài)修改
const class_ro_t *safe_ro() //存儲(chǔ)成員變量,不可動(dòng)態(tài)修改魔熏,類似readonly

3衷咽、類的方法、屬性蒜绽、協(xié)議分別存在哪里

struct class_rw_t {

const class_ro_t *ro() const {//ro
        auto v = get_ro_or_rwe();
        if (slowpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>()->ro;
        }
        return v.get<const class_ro_t *>();
    }

    const method_array_t methods() const {//方法列表
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>()->methods;
        } else {
            return method_array_t{v.get<const class_ro_t *>()->baseMethods()};
        }
    }

    const property_array_t properties() const {//屬性列表
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>()->properties;
        } else {
            return property_array_t{v.get<const class_ro_t *>()->baseProperties};
        }
    }

    const protocol_array_t protocols() const {//協(xié)議列表
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>()->protocols;
        } else {
            return protocol_array_t{v.get<const class_ro_t *>()->baseProtocols};
        }
    }
    ...
};

4镶骗、如何讀取類中的方法、屬性躲雅、協(xié)議

屬性鼎姊、方法、協(xié)議都存在rw中,所以我們先需要獲取rw

(lldb) p/x MCPerson.class
(Class) $0 = 0x0000000100002180 MCPerson
(lldb) p/x 0x0000000100002180 + 0x20
(long) $1 = 0x00000001000021a0
(lldb) p (class_data_bits_t *)$1
(class_data_bits_t *) $2 = 0x00000001000021a0
(lldb) p (class_rw_t*)$2->data()
(class_rw_t *) $3 = 0x000000010073b800//屬性方法都在這里面
  • 獲取方法
(lldb) p $3->methods() //獲取方法
(const method_array_t) $4 = {
  list_array_tt<method_t, method_list_t> = {
     = {
      list = 0x00000001000020c0
      arrayAndFlag = 4294975680
    }
  }
}
(lldb) p $4.list
(method_list_t *const) $5 = 0x00000001000020c0
(lldb) p *$5//打印指針的內(nèi)容
(method_list_t) $6 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 3
    first = {
      name = ".cxx_destruct"
      types = 0x0000000100000fa0 "v16@0:8"
      imp = 0x0000000100000e50 (KCObjc`-[MCPerson .cxx_destruct] at main.m:16)
    }
  }
}
(lldb) p $6.get(0)// 獲取第1個(gè)方法
(method_t) $7 = {
  name = ".cxx_destruct"
  types = 0x0000000100000fa0 "v16@0:8"
  imp = 0x0000000100000e50 (KCObjc`-[MCPerson .cxx_destruct] at main.m:16)
(lldb) p $6.get(1)
(method_t) $8 = {
  name = "name"
  types = 0x0000000100000f8d "@16@0:8"
  imp = 0x0000000100000df0 (KCObjc`-[MCPerson name] at main.m:13)
}
(lldb) p $6.get(2)
(method_t) $9 = {
  name = "setName:"
  types = 0x0000000100000f95 "v24@0:8@16"
  imp = 0x0000000100000e20 (KCObjc`-[MCPerson setName:] at main.m:13)
}
  • 獲取屬性
(lldb) p $3->properties()//獲取屬性
(const property_array_t) $10 = {
  list_array_tt<property_t, property_list_t> = {
     = {
      list = 0x0000000100002138
      arrayAndFlag = 4294975800
    }
  }
}
(lldb) p $10.list
(property_list_t *const) $11 = 0x0000000100002138
(lldb) p *$11
(property_list_t) $12 = {
  entsize_list_tt<property_t, property_list_t, 0> = {
    entsizeAndFlags = 16
    count = 1
    first = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
  }
}
(lldb) p $12->get(0)
(property_t) $13 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
  Fix-it applied, fixed expression was: 
    $12.get(0)
(lldb) p $12.get(0)
(property_t) $14 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
  • 獲取協(xié)議
(lldb) p $3->protocols()//獲取協(xié)議
(const protocol_array_t) $15 = {
  list_array_tt<unsigned long, protocol_list_t> = {
     = {
      list = 0x0000000000000000//全是0相寇,表明沒(méi)有慰于,如果有跟上面的步驟一致
      arrayAndFlag = 0
    }
  }
}

4、類的成員變量存在哪里

拓展唤衫,成員變量沒(méi)有存在rw中婆赠,存在ro中

5、總結(jié)

萬(wàn)物皆對(duì)象:類的本質(zhì)就是對(duì)象佳励,類也是對(duì)象(類對(duì)象)
類在 class_rw_t 結(jié)構(gòu)中存儲(chǔ)了編譯時(shí)確定的屬性休里、方法和協(xié)議等內(nèi)容。
實(shí)例方法存放在類中赃承,類方法存在元類中

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末妙黍,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子瞧剖,更是在濱河造成了極大的恐慌拭嫁,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抓于,死亡現(xiàn)場(chǎng)離奇詭異做粤,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)捉撮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門驮宴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人呕缭,你說(shuō)我怎么就攤上這事⌒藜海” “怎么了恢总?”我有些...
    開封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)睬愤。 經(jīng)常有香客問(wèn)我片仿,道長(zhǎng),這世上最難降的妖魔是什么尤辱? 我笑而不...
    開封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任砂豌,我火速辦了婚禮,結(jié)果婚禮上光督,老公的妹妹穿的比我還像新娘阳距。我一直安慰自己,他們只是感情好结借,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開白布筐摘。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪咖熟。 梳的紋絲不亂的頭發(fā)上圃酵,一...
    開封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音馍管,去河邊找鬼郭赐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛确沸,可吹牛的內(nèi)容都是我干的捌锭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼张惹,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼舀锨!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起宛逗,我...
    開封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤坎匿,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后雷激,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體替蔬,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年屎暇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了承桥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡根悼,死狀恐怖凶异,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情挤巡,我是刑警寧澤剩彬,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站矿卑,受9級(jí)特大地震影響喉恋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜母廷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一轻黑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧琴昆,春花似錦氓鄙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)把介。三九已至,卻和暖如春蟋座,著一層夾襖步出監(jiān)牢的瞬間拗踢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工向臀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留巢墅,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓券膀,卻偏偏與公主長(zhǎng)得像君纫,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子芹彬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355