類的結(jié)構(gòu)探一探

對象、類寂曹、元類读处、根元類的關(guān)系

先溫習(xí)下這個(gè)圖

先背背圖

  • 類的本質(zhì)

首先準(zhǔn)備可調(diào)試objc源碼,配置可執(zhí)行的objc源碼

#import <Foundation/Foundation.h>
#import "PHPerson.h"
#import <objc/runtime.h>
int main(int argc, const char * argv[]) {
    @autoreleasepool {
       
        PHPerson * phPerson = [PHPerson alloc];
        Class pClass = object_getClass(phPerson);
        
    }
    return 0;
}

cd到main.m所在目錄clang命令:
clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk main.m

main.m文件輸出.cpp如圖


PHPerson * phPerson = ((PHPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("PHPerson"), sel_registerName("alloc"));

Class是什么?在objc源碼找到
typedef struct objc_class *Class探知Class的類型是objc_class.

那么 objc_class又是什么碌燕?
繼續(xù)跟進(jìn):


可知objc_class繼承于objc_object

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() const {
        return bits.data();
    }
....//省略
}

總結(jié): 類的本質(zhì)是objc_class類型的結(jié)構(gòu)體折汞,objc_class繼承于objc_object倔幼;

問題:不是說對象繼承與NSObject?跟objc_object有什么關(guān)系爽待?

1.同樣在objc源碼中找到NSObject的定義

@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}

NSObject中存在Class類型的成員變量isa损同,Class又是objc_class類型的結(jié)構(gòu)體且objc_class繼承于objc_object的結(jié)構(gòu)體;

結(jié)論:NSObject類是OC版本objc_object

問題:isa明明是isa_t類型的,為什么注釋了一句Class ISA鸟款?
1.萬物皆對象膏燃,用繼承于objc_objectClass接收是沒問題的
2.強(qiáng)轉(zhuǎn),方便isa走位時(shí)返回類的類型

  • 類的結(jié)構(gòu)

objc_class的定義

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() const {
        return bits.data();
    }
....
}

1.Class ISA

  • 不但實(shí)例對象中有isa指針何什,類對象中也有isa指針關(guān)聯(lián)著元類,
    Class本身就是一個(gè)指針组哩,占用8字節(jié)

2.Class superclass

  • 顧名思義就是類的父類(一般為NSObjectsuperclassClass類型,所以占用8字節(jié)

3.cache_t cache
-cache_t是一個(gè)結(jié)構(gòu)體,內(nèi)存長度有所有元素決定:_buckets是一個(gè)指針伶贰,占用8字節(jié)蛛砰;mask_t是個(gè)int類型_mask占用4字節(jié)黍衙;_occupied占用4字節(jié)


struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
    explicit_atomic<struct bucket_t *> _buckets;//8字節(jié)
    explicit_atomic<mask_t> _mask;//4字節(jié)
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    explicit_atomic<uintptr_t> _maskAndBuckets;
#if __LP64__
    uint16_t _flags; //2字節(jié)
#endif
    uint16_t _occupied;//2字節(jié)
  ...
};

typedef uint32_t mask_t;  // x86_64 & arm64 asm are less efficient with 16-bits

合計(jì):cache_t占用16字節(jié)

4.class_data_bits_t bits
根據(jù)字母意思就是大小內(nèi)存猜下也就是存數(shù)據(jù)

struct class_data_bits_t {
    friend objc_class;

    // Values are the FAST_ flags above.
    uintptr_t bits;
private:
    bool getBit(uintptr_t bit) const
    {
        return bits & bit;
    }

    void setBits(uintptr_t set) {
        __c11_atomic_fetch_or((_Atomic(uintptr_t) *)&bits, set, __ATOMIC_RELAXED);
    }

public:

    class_rw_t* data() const {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
    void setData(class_rw_t *newData)
    {
        ASSERT(!data()  ||  (newData->flags & (RW_REALIZING | RW_FUTURE)));
        // Set during realization or construction only. No locking needed.
        // Use a store-release fence because there may be concurrent
        // readers of data and data's contents.
        uintptr_t newBits = (bits & ~FAST_DATA_MASK) | (uintptr_t)newData;
        atomic_thread_fence(memory_order_release);
        bits = newBits;
    }
};

class_data_bits_t 存儲(chǔ)的就是類相關(guān)的東西

 class_rw_t* data() const {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }

點(diǎn)擊進(jìn)入源碼


struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint16_t witness;
#if SUPPORT_INDEXED_ISA
    uint16_t index;
#endif

    explicit_atomic<uintptr_t> ro_or_rw_ext;

    Class firstSubclass;
    Class nextSiblingClass;

private:
    using ro_or_rw_ext_t = objc::PointerUnion<const class_ro_t *, class_rw_ext_t *>;

    const ro_or_rw_ext_t get_ro_or_rwe() const {
        return ro_or_rw_ext_t{ro_or_rw_ext};
    }

    void set_ro_or_rwe(const class_ro_t *ro) {
        ro_or_rw_ext_t{ro}.storeAt(ro_or_rw_ext, memory_order_relaxed);
    }

    void set_ro_or_rwe(class_rw_ext_t *rwe, const class_ro_t *ro) {
        // the release barrier is so that the class_rw_ext_t::ro initialization
        // is visible to lockless readers
        rwe->ro = ro;
        ro_or_rw_ext_t{rwe}.storeAt(ro_or_rw_ext, memory_order_release);
    }

    class_rw_ext_t *extAlloc(const class_ro_t *ro, bool deep = false);

public:
    void setFlags(uint32_t set)
    {
        __c11_atomic_fetch_or((_Atomic(uint32_t) *)&flags, set, __ATOMIC_RELAXED);
    }

    void clearFlags(uint32_t clear) 
    {
        __c11_atomic_fetch_and((_Atomic(uint32_t) *)&flags, ~clear, __ATOMIC_RELAXED);
    }

    // set and clear must not overlap
    void changeFlags(uint32_t set, uint32_t clear) 
    {
        ASSERT((set & clear) == 0);

        uint32_t oldf, newf;
        do {
            oldf = flags;
            newf = (oldf | set) & ~clear;
        } while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
    }

    class_rw_ext_t *ext() const {
        return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>();
    }

    class_rw_ext_t *extAllocIfNeeded() {
        auto v = get_ro_or_rwe();
        if (fastpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>();
        } else {
            return extAlloc(v.get<const class_ro_t *>());
        }
    }

    class_rw_ext_t *deepCopy(const class_ro_t *ro) {
        return extAlloc(ro, true);
    }

    const class_ro_t *ro() const {//成員變量(猜的)
        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 *>();
    }

    void set_ro(const class_ro_t *ro) {//類方法列表(猜)
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            v.get<class_rw_ext_t *>()->ro = ro;
        } else {
            set_ro_or_rwe(ro);
        }
    }

    const method_array_t methods() const {//實(shí)例方法列表
        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};
        }
    }
};

這個(gè)信息也很重要 特別是ro()

struct class_rw_ext_t {
    const class_ro_t *ro;
    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;
    char *demangledName;
    uint32_t version;
};
  • 類的相關(guān)方法


如圖添加成員變量nickName泥畅,屬性name,類方法+ (void)walk 和實(shí)例方法- (void)fly

類結(jié)構(gòu)打印

bits剛好是類的內(nèi)存首地址+isa、superclass琅翻、cache的內(nèi)存長度==>0x100003250+32字節(jié) =0x100003270

類信息地址打印

類信息地址打印

通過指針地址訪問data()獲取類信息的數(shù)據(jù)

獲取類信息的數(shù)據(jù)

訪問class_rw_tproperties屬性得到對象的屬性,可以看出是個(gè)數(shù)組類型property_array_t

image.png

獲取這跟個(gè)屬性列表位仁,刺死的$5是數(shù)組的第一個(gè)數(shù)據(jù)的地址也是數(shù)組的首地址,通過下標(biāo)指向順序訪問列表數(shù)據(jù)
image.png

變量中為啥只有name沒有nickName方椎,猜測這個(gè)成員變量nickName還是在這塊存著聂抢,發(fā)現(xiàn)這個(gè)玩意兒

const class_ro_t *ro() const {//成員變量(猜的)
        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 *>();
    }

底層:


struct protocol_list_t {
    // count is pointer-sized by accident.
    uintptr_t count;
    protocol_ref_t list[0]; // variable-size

    size_t byteSize() const {
        return sizeof(*this) + count*sizeof(list[0]);
    }

    protocol_list_t *duplicate() const {
        return (protocol_list_t *)memdup(this, this->byteSize());
    }

    typedef protocol_ref_t* iterator;
    typedef const protocol_ref_t* const_iterator;

    const_iterator begin() const {
        return list;
    }
    iterator begin() {
        return list;
    }
    const_iterator end() const {
        return list + count;
    }
    iterator end() {
        return list + count;
    }
};

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;//這是存在成員變量的數(shù)組

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    // This field exists only when RO_HAS_SWIFT_INITIALIZER is set.
    _objc_swiftMetadataInitializer __ptrauth_objc_method_list_imp _swiftMetadataInitializer_NEVER_USE[0];

    _objc_swiftMetadataInitializer swiftMetadataInitializer() const {
        if (flags & RO_HAS_SWIFT_INITIALIZER) {
            return _swiftMetadataInitializer_NEVER_USE[0];
        } else {
            return nil;
        }
    }

    method_list_t *baseMethods() const {
        return baseMethodList;
    }

    class_ro_t *duplicate() const {
        if (flags & RO_HAS_SWIFT_INITIALIZER) {
            size_t size = sizeof(*this) + sizeof(_swiftMetadataInitializer_NEVER_USE[0]);
            class_ro_t *ro = (class_ro_t *)memdup(this, size);
            ro->_swiftMetadataInitializer_NEVER_USE[0] = this->_swiftMetadataInitializer_NEVER_USE[0];
            return ro;
        } else {
            size_t size = sizeof(*this);
            class_ro_t *ro = (class_ro_t *)memdup(this, size);
            return ro;
        }
    }
};

中的這個(gè)const ivar_list_t * ivars

image.png

依然沒有找到nickName

通過訪問ivars

終于找到

這個(gè)ivars的列表中里具體都有啥?

image.png

  • 類的方法

打印robaseMethodList

image.png

image.png

+walk()類方法找不到了
找元類的方法試下:

image.png

總結(jié):類方法可以理解成元類對象實(shí)例方法,因此存在元類

  • 成員變量存放在ivar
  • 屬性存放在property棠众,同時(shí)也會(huì)存一份在ivar涛浙,并生成setter、getter方法
  • 對象方法存放在里面
  • 類方法存放在元類里面
  • 利用底層開放的API可以驗(yàn)證以上結(jié)論
void testObjc_copyIvar_copyProperies(Class cls) {
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList(cls, &count);
    for (unsigned int i=0; i < count; i++) {
        Ivar const ivar = ivars[i];
        //獲取實(shí)例變量名
        const char*cName = ivar_getName(ivar);
        NSString *ivarName = [NSString stringWithUTF8String:cName];
        NSLog(@"class_copyIvarList:%@",ivarName);
    }
    free(ivars);

    unsigned int pCount = 0;
    objc_property_t *properties = class_copyPropertyList(cls, &pCount);
    for (unsigned int i=0; i < pCount; i++) {
        objc_property_t const property = properties[i];
        //獲取屬性名
        NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
        //獲取屬性值
        NSLog(@"class_copyProperiesList:%@",propertyName);
    }
    free(properties);
}

void testObjc_copyMethodList(Class cls) {
    unsigned int count = 0;
    Method *methods = class_copyMethodList(cls, &count);
    for (unsigned int i=0; i < count; i++) {
        Method const method = methods[i];
        //獲取方法名
        NSString *key = NSStringFromSelector(method_getName(method));
        
        NSLog(@"Method, name: %@", key);
    }
    free(methods);
}

void testInstanceMethod_classToMetaclass(Class cls) {
    const char *className = class_getName(cls);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getInstanceMethod(cls, @selector(walk));
    Method method2 = class_getInstanceMethod(metaClass, @selector(fly));

    Method method3 = class_getInstanceMethod(cls, @selector(walk));
    Method method4 = class_getInstanceMethod(metaClass, @selector(fly));
    
    NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);
    NSLog(@"%s",__func__);
}

void testClassMethod_classToMetaclass(Class cls) {
    const char *className = class_getName(cls);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getClassMethod(cls, @selector(walk));
    Method method2 = class_getClassMethod(metaClass, @selector(fly));

    Method method3 = class_getClassMethod(cls, @selector(walk));
    Method method4 = class_getClassMethod(metaClass, @selector(fly));
    
    NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);
    NSLog(@"%s",__func__);
}

void testIMP_classToMetaclass(Class cls) {
    const char *className = class_getName(cls);
    Class metaClass = objc_getMetaClass(className);

    IMP imp1 = class_getMethodImplementation(cls, @selector(walk));
    IMP imp2 = class_getMethodImplementation(metaClass, @selector(fly));

    IMP imp3 = class_getMethodImplementation(cls, @selector(walk));
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(fly));

    NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
    NSLog(@"%s",__func__);
}

  • 其實(shí)直接用clang編譯也能看出點(diǎn)東西
  • 關(guān)于v@:這個(gè)玩意摄欲,在蘋果開發(fā)者文檔上也有介紹
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市疮薇,隨后出現(xiàn)的幾起案子胸墙,更是在濱河造成了極大的恐慌,老刑警劉巖按咒,帶你破解...
    沈念sama閱讀 223,126評論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件迟隅,死亡現(xiàn)場離奇詭異,居然都是意外死亡励七,警方通過查閱死者的電腦和手機(jī)智袭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評論 3 400
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來掠抬,“玉大人吼野,你說我怎么就攤上這事×讲ǎ” “怎么了瞳步?”我有些...
    開封第一講書人閱讀 169,941評論 0 366
  • 文/不壞的土叔 我叫張陵,是天一觀的道長腰奋。 經(jīng)常有香客問我单起,道長,這世上最難降的妖魔是什么劣坊? 我笑而不...
    開封第一講書人閱讀 60,294評論 1 300
  • 正文 為了忘掉前任嘀倒,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘测蘑。我一直安慰自己灌危,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,295評論 6 398
  • 文/花漫 我一把揭開白布帮寻。 她就那樣靜靜地躺著乍狐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪固逗。 梳的紋絲不亂的頭發(fā)上浅蚪,一...
    開封第一講書人閱讀 52,874評論 1 314
  • 那天,我揣著相機(jī)與錄音烫罩,去河邊找鬼惜傲。 笑死,一個(gè)胖子當(dāng)著我的面吹牛贝攒,可吹牛的內(nèi)容都是我干的盗誊。 我是一名探鬼主播,決...
    沈念sama閱讀 41,285評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼隘弊,長吁一口氣:“原來是場噩夢啊……” “哼哈踱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起梨熙,我...
    開封第一講書人閱讀 40,249評論 0 277
  • 序言:老撾萬榮一對情侶失蹤开镣,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后咽扇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體邪财,經(jīng)...
    沈念sama閱讀 46,760評論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,840評論 3 343
  • 正文 我和宋清朗相戀三年质欲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了树埠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,973評論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嘶伟,死狀恐怖怎憋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情九昧,我是刑警寧澤盛霎,帶...
    沈念sama閱讀 36,631評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站耽装,受9級特大地震影響愤炸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜掉奄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,315評論 3 336
  • 文/蒙蒙 一规个、第九天 我趴在偏房一處隱蔽的房頂上張望凤薛。 院中可真熱鬧,春花似錦诞仓、人聲如沸缤苫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽活玲。三九已至,卻和暖如春谍婉,著一層夾襖步出監(jiān)牢的瞬間舒憾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評論 1 275
  • 我被黑心中介騙來泰國打工穗熬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留镀迂,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,431評論 3 379
  • 正文 我出身青樓唤蔗,卻偏偏與公主長得像探遵,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子妓柜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,982評論 2 361