iOS底層原理總結(jié) - 探尋Class的本質(zhì)

本篇主要是對(duì)小碼哥底層視頻學(xué)習(xí)的總結(jié)棒拂。方便日后復(fù)習(xí)。
上一篇《iOS底層原理-探尋OC對(duì)象本質(zhì)》:http://www.reibang.com/p/e5e2e3747be7

本篇學(xué)習(xí)總結(jié):

  • 探尋Class的本質(zhì)
  • 代碼求證Class正確性

好了蛛蒙,帶著問(wèn)題,我們一一開(kāi)始閱讀吧 ??

一.探尋Class的本質(zhì)

我們知道不管是類(lèi)對(duì)象還是元類(lèi)對(duì)象渤愁,類(lèi)型都是Class類(lèi)型牵祟,底層都是objc_class結(jié)構(gòu)體的指針,我們今天就來(lái)探究一下這個(gè)結(jié)構(gòu)體抖格。

先上代碼

Class objectClass = [NSObject class];        
Class objectMetaClass = object_getClass([NSObject class]);

點(diǎn)擊Class可以看到如下結(jié)構(gòu):

typedef struct objc_class *Class;

繼續(xù)點(diǎn)擊objc_class可以看到如下結(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;

這部分代碼經(jīng)撑灯唬看到,但是OBJC2_UNAVAILABLE說(shuō)明代碼已經(jīng)過(guò)時(shí)了雹拄,那么最新的struct objc_class結(jié)構(gòu)體是什么樣的呢收奔,我們還是通過(guò)下載objc源代碼找到。
我們下載這個(gè)版本的源碼:

源碼文件夾.png

然后打開(kāi)工程滓玖,搜索typedef struct objc_class坪哄,查找結(jié)果如下:
搜索結(jié)果.png

隨便點(diǎn)擊一個(gè)搜索結(jié)果就可以,我們可以看到新的struct objc_class : objc_object的結(jié)構(gòu)如下:
objc_object結(jié)構(gòu)體部分信息.png

struct objc_class : objc_object這種寫(xiě)法類(lèi)似c++類(lèi)寫(xiě)法势篡,里面有方法翩肌,成員變量,我們點(diǎn)擊objc_object禁悠,它的結(jié)構(gòu)如下:
objc_object結(jié)構(gòu)體部分信息.png

完整的類(lèi)信息有點(diǎn)長(zhǎng)念祭,只截取有用的一部分信息吧,我們從這段代碼中看到了
objc_object的isa變量.png

我們猜測(cè)碍侦,凡是繼承struct objc_object的子結(jié)構(gòu)體,應(yīng)該都包含一個(gè)isa變量粱坤。
我們?cè)谏弦黄呀?jīng)總結(jié)了隶糕,類(lèi)中有成員變量信息(ivar),類(lèi)的屬性信息(property)站玄,類(lèi)的方法列表信息(method /instance list)若厚,類(lèi)的協(xié)議信息(protocol),但從objc_class中我們沒(méi)有看出這部分信息呢蜒什,別著急测秸,我們點(diǎn)擊class_rw_t看一下,結(jié)構(gòu)如下圖所示:

class_rw_t結(jié)構(gòu)體信息.png

那成員變量信息呢灾常,繼續(xù)點(diǎn)擊class_ro_t霎冯,結(jié)構(gòu)如下圖所示:

class_ro_t結(jié)構(gòu)體信息.png

到這里,基本上要看的數(shù)據(jù)信息都已經(jīng)看完了钞瀑,但是可能會(huì)有點(diǎn)蒙沈撞,我們用一張圖進(jìn)行總結(jié)吧:
窺探struct objc_class結(jié)構(gòu).png

二.代碼求證Class正確性

用代碼求證系統(tǒng)封閉結(jié)構(gòu)體內(nèi)部信息時(shí),我們直接獲取可能拿不到雕什,這時(shí)候我們一般采用自定義一個(gè)具有類(lèi)似成員變量的結(jié)構(gòu)體缠俺,當(dāng)我們強(qiáng)制轉(zhuǎn)化系統(tǒng)結(jié)構(gòu)體為自定義結(jié)構(gòu)體的時(shí)候,就能一一對(duì)應(yīng)的賦值贷岸,此時(shí)壹士,我們可以拿到自定義結(jié)構(gòu)體內(nèi)部的消息。
下面是仿照系統(tǒng)objc_class結(jié)構(gòu)體偿警,自定義的結(jié)構(gòu)體躏救,當(dāng)然了這是小碼哥抽離好的,直接用可以螟蒸,創(chuàng)建一個(gè)MJClassInfo.h文件

#import <Foundation/Foundation.h>

#ifndef MJClassInfo_h
#define MJClassInfo_h

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
# endif

#if __LP64__
typedef uint32_t mask_t;
#else
typedef uint16_t mask_t;
#endif
typedef uintptr_t cache_key_t;

struct bucket_t {
    cache_key_t _key;
    IMP _imp;
};

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

struct entsize_list_tt {
    uint32_t entsizeAndFlags;
    uint32_t count;
};

struct method_t {
    SEL name;
    const char *types;
    IMP imp;
};

struct method_list_t : entsize_list_tt {
    method_t first;
};

struct ivar_t {
    int32_t *offset;
    const char *name;
    const char *type;
    uint32_t alignment_raw;
    uint32_t size;
};

struct ivar_list_t : entsize_list_tt {
    ivar_t first;
};

struct property_t {
    const char *name;
    const char *attributes;
};

struct property_list_t : entsize_list_tt {
    property_t first;
};

struct chained_property_list {
    chained_property_list *next;
    uint32_t count;
    property_t list[0];
};

typedef uintptr_t protocol_ref_t;
struct protocol_list_t {
    uintptr_t count;
    protocol_ref_t list[0];
};

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;  // instance對(duì)象占用的內(nèi)存空間
#ifdef __LP64__
    uint32_t reserved;
#endif
    const uint8_t * ivarLayout;
    const char * name;  // 類(lèi)名
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;  // 成員變量列表
    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
};

struct class_rw_t {
    uint32_t flags;
    uint32_t version;
    const class_ro_t *ro;
    method_list_t * methods;    // 方法列表
    property_list_t *properties;    // 屬性列表
    const protocol_list_t * protocols;  // 協(xié)議列表
    Class firstSubclass;
    Class nextSiblingClass;
    char *demangledName;
};

#define FAST_DATA_MASK          0x00007ffffffffff8UL
struct class_data_bits_t {
    uintptr_t bits;
public:
    class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
};

/* OC對(duì)象 */
struct mj_objc_object {
    void *isa;
};

/* 類(lèi)對(duì)象 */
struct mj_objc_class : mj_objc_object {
    Class superclass;
    cache_t cache;
    class_data_bits_t bits;
public:
    class_rw_t* data() {
        return bits.data();
    }
    
    mj_objc_class* metaClass() {
        return (mj_objc_class *)((long long)isa & ISA_MASK);
    }
};

#endif /* MJClassInfo_h */

我們需要將main.m 文件后綴修改為main.mm(告訴編譯器兼容oc代碼跟c++代碼).
我們?cè)趍ain.mm文件中寫(xiě)好要強(qiáng)轉(zhuǎn)的代碼:

// objective-c++
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "MJClassInfo.h"

// MJPerson
@interface MJPerson : NSObject <NSCopying>
{
@public
    int _age;
}
@property (nonatomic, assign) int no;
- (void)personInstanceMethod;
+ (void)personClassMethod;
@end

@implementation MJPerson

- (void)test
{
    
}

- (void)personInstanceMethod
{
    
}
+ (void)personClassMethod
{
    
}
- (id)copyWithZone:(NSZone *)zone
{
    return nil;
}
@end

// MJStudent
@interface MJStudent : MJPerson <NSCoding>
{
@public
    int _weight;
}
@property (nonatomic, assign) int height;
- (void)studentInstanceMethod;
+ (void)studentClassMethod;
@end

@implementation MJStudent
- (void)test
{
    
}
- (void)studentInstanceMethod
{
    
}
+ (void)studentClassMethod
{
    
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
    return nil;
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    
}
@end


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MJStudent *stu = [[MJStudent alloc] init];
        stu->_weight = 10;
        Class objectClass = [NSObject class];
        Class objectMetaClass = object_getClass([NSObject class]);
        
        mj_objc_class *studentClass = (__bridge mj_objc_class *)([MJStudent class]);
        mj_objc_class *personClass = (__bridge mj_objc_class *)([MJPerson class]);
        
        class_rw_t *studentClassData = studentClass->data();
        class_rw_t *personClassData = personClass->data();
        
        class_rw_t *studentMetaClassData = studentClass->metaClass()->data();
        class_rw_t *personMetaClassData = personClass->metaClass()->data();

        NSLog(@"1111");
    }
    return 0;
}

最終工程中有兩個(gè)文件


結(jié)構(gòu)體強(qiáng)轉(zhuǎn)文件.png

然后我們通過(guò)打斷點(diǎn)依次分析如下:
instance對(duì)象
首先我們來(lái)看instance對(duì)象盒使,我們知道instance對(duì)象存儲(chǔ)isa指針跟成員變量數(shù)據(jù),并且instance對(duì)象的isa指針是指向class對(duì)象的七嫌,果然如下圖所示:

instance對(duì)象一.png

instance對(duì)象二.png

Class對(duì)象
接著我們來(lái)看class對(duì)象少办,同樣通過(guò)上一篇文章,我們明確class對(duì)象中存儲(chǔ)著isa指針诵原,superclass指針英妓,以及類(lèi)的屬性信息,類(lèi)的成員變量信息皮假,類(lèi)的對(duì)象方法鞋拟,和類(lèi)的協(xié)議信息,而通過(guò)上面對(duì)object源碼的分析惹资,我們知道這些信息存儲(chǔ)在class對(duì)象的class_rw_t中,我們通過(guò)強(qiáng)制轉(zhuǎn)化來(lái)窺探其中的內(nèi)容航闺。如下圖:

class對(duì)象.png

meta-class對(duì)象
最后我們來(lái)看meta-class元類(lèi)對(duì)象褪测,上文提到meta-class中存儲(chǔ)著isa指針猴誊,superclass指針,以及類(lèi)的類(lèi)方法信息侮措。同時(shí)我們知道m(xù)eta-class元類(lèi)對(duì)象與class類(lèi)對(duì)象懈叹,具有相同的結(jié)構(gòu),只不過(guò)存儲(chǔ)的信息不同分扎,并且元類(lèi)對(duì)象的isa指針指向基類(lèi)的元類(lèi)對(duì)象澄成,基類(lèi)的元類(lèi)對(duì)象的isa指針指向自己。元類(lèi)對(duì)象的superclass指針指向其父類(lèi)的元類(lèi)對(duì)象畏吓,基類(lèi)的元類(lèi)對(duì)象的superclass指針指向其類(lèi)對(duì)象墨状。

meta-class對(duì)象.png

本篇學(xué)習(xí)先記錄到此,感謝閱讀菲饼,如有錯(cuò)誤肾砂,不吝賜教。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宏悦,一起剝皮案震驚了整個(gè)濱河市镐确,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌饼煞,老刑警劉巖源葫,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異砖瞧,居然都是意外死亡臼氨,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)芭届,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)储矩,“玉大人,你說(shuō)我怎么就攤上這事褂乍〕炙恚” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵逃片,是天一觀的道長(zhǎng)屡拨。 經(jīng)常有香客問(wèn)我,道長(zhǎng)褥实,這世上最難降的妖魔是什么呀狼? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮损离,結(jié)果婚禮上哥艇,老公的妹妹穿的比我還像新娘。我一直安慰自己僻澎,他們只是感情好貌踏,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布十饥。 她就那樣靜靜地躺著,像睡著了一般祖乳。 火紅的嫁衣襯著肌膚如雪逗堵。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,079評(píng)論 1 285
  • 那天眷昆,我揣著相機(jī)與錄音蜒秤,去河邊找鬼。 笑死亚斋,一個(gè)胖子當(dāng)著我的面吹牛作媚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播伞访,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼掂骏,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了厚掷?” 一聲冷哼從身側(cè)響起弟灼,我...
    開(kāi)封第一講書(shū)人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎冒黑,沒(méi)想到半個(gè)月后田绑,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡抡爹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年掩驱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冬竟。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡欧穴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出泵殴,到底是詐尸還是另有隱情涮帘,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布笑诅,位于F島的核電站调缨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏吆你。R本人自食惡果不足惜弦叶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望妇多。 院中可真熱鬧伤哺,春花似錦、人聲如沸砌梆。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)咸包。三九已至桃序,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間烂瘫,已是汗流浹背媒熊。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坟比,地道東北人芦鳍。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像葛账,于是被迫代替她去往敵國(guó)和親柠衅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

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

  • iOS底層原理總結(jié) - 探尋Class的本質(zhì) 對(duì)小碼哥底層班視頻學(xué)習(xí)的總結(jié)與記錄籍琳。面試題部分菲宴,通過(guò)對(duì)面試題的分析探...
    xx_cc閱讀 11,594評(píng)論 9 48
  • iOS底層原理總結(jié) - 探尋OC對(duì)象的本質(zhì) 原文鏈接 對(duì)小碼哥底層班視頻學(xué)習(xí)的總結(jié)與記錄。面試題部分趋急,通過(guò)對(duì)面試題...
    二斤寂寞閱讀 646評(píng)論 0 4
  • 直到我吃了很久的三文魚(yú)刺身呜达,我才直到谣蠢,原來(lái)三文魚(yú)也有生活在淡水的。 第一次接觸到淡水三文魚(yú)是在云南查近,在大理古城游蕩...
    旋仔的文藝小站閱讀 212評(píng)論 0 0
  • 今天聽(tīng)了楊校長(zhǎng)的講話我很感動(dòng)眉踱,38年的堅(jiān)守?fù)Q來(lái)了今日的功成名就,他不忘初心霜威,實(shí)現(xiàn)了最初的承諾谈喳,也見(jiàn)證了他...
    孫微sw閱讀 226評(píng)論 0 8
  • 晚上十一點(diǎn)三十分今天下班,走過(guò)火車(chē)站長(zhǎng)長(zhǎng)的地下通道侥祭,一個(gè)女人出現(xiàn)在我的視線里叁执,她望著地上的一個(gè)玩具靜靜地站在那里。...
    郵戳的腳步閱讀 126評(píng)論 0 0