iOS底層探究-類的結構分析

前言

上篇博客說完了對象的成員博客-isa結構分析,今天我們來研究一下對象的爸爸,他就是,相信大家都知道對象是由一個類通過調(diào)用(alloc init new)等方法創(chuàng)建的。那么里面都有哪些默認屬性,今天我們就通過代碼來看一下。

1.代碼

我們打開之前博客用到的objc源碼的項目,objc源碼地址Source Browser,然后在main.m文件新建兩個類Farther和Son,Son繼承于Father,F(xiàn)ather繼承于NSObject,代碼如下。


#import <Foundation/Foundation.h>
#import <objc/objc.h>

@interface Father : NSObject
{
    NSString *hobby;
}
@property (nonatomic, copy) NSString *name;

- (void)sayHi;
+ (void)sayGoodBye;
@end
@implementation Father
- (void)sayHi {
    NSLog(@"%s", __func__);
}
+ (void)sayGoodBye {
    NSLog(@"%s", __func__);
}
@end

@interface Son : Father
@end
@implementation Son
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        Son *s = [[Son alloc] init];
        s.name = @"張山";
    }
    return 0;
}

然后我們用Clang編譯器編譯main.m文件 成main.cpp,具體方法在上篇博客里面,鏈接在上面。
然后我們打開main.cpp文件,全局搜索struct objc_object,然后發(fā)現(xiàn)7660行定義了*Class是繼承于結構體objc_class。

image.png

接下來我們在代碼里找不到objc_class定義了,這個時候我們就得去找蘋果提供開源的objc4-781源碼了,在蘋果網(wǎng)站下載Source Browser
在源碼里直接搜索struct objc_class,然后定位到 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() const {
        return bits.data();
    }
    
    // 省略部分代碼.......
}

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

我們發(fā)現(xiàn)找到了結構體objc_class的定義,它繼承于objc_object,萬物皆對象,萬物皆繼承objc_object,類之間的繼承關系如下圖,最后都走向了root class也就是NSObject->objc_class->objc_object

image.png

image.png

我們接下來看下objc_class里面定義哪些成員。
1.isa 指針(繼承自objc_object)
2.Class superclass;指針
3.cache_t cache;緩存指針和函數(shù)表
4.class_data_bits_t bits;class_rw_t加上自定義的屬性、方法,初始化的標記..

1.isa 指針

這個不多講了,之前博客講過isa的用途和結構

2.Class superclass;指針

superclass也就是字面意思,它指向這個類的父類,比如上面LGSon里面的superclass就會指向它的父類LGFather,LGFather里面的superclass就會指向所有類的根父類NSObject

3.cache緩存

cache 結構為 cache_t,其定義如下:

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;
    
#if __LP64__
    uint16_t _flags;
#endif
    uint16_t _occupied;
}

// 省略部分代碼...

在這里我們計算一下cache所占用內(nèi)存大小,其中if elseif兩個走向都是16字節(jié)。那么cache就是占16字節(jié)。接下來要用到。

image.png

4.bits

bits 的數(shù)據(jù)結構類型是 class_data_bits_t,同時也是一個結構體類型。它存儲著類的屬性,實例方法等等,接下來我們就來驗證一下。
我們都知道對象的指針指向對象的首地址也就是第一個屬性isa。而對象的存儲是連續(xù)的,也就是從isa地址往后打印就是對象的其他數(shù)據(jù)成員,而類對象也是對象,第一個isa8字節(jié),superclass指針也是8字節(jié),cache上面計算了是16字節(jié),加起來是32字節(jié),那么bit所在內(nèi)存地址應該是對象首地址+32字節(jié)。
我們隨意打個斷點,然后控制臺輸入命令p/x LGFather.class打印類對象LGFather的16進制地址得到0x0000000100002320。

image.png

然后我們拿首地址0x0000000100002320+32,32是十進制轉成16進制是0x20,0x0000000100002320 + 0x20 = 0x0000000100002340,我們打印下這個地址驗證一下。

image.png

我們看到objc_class里面的data()返回的bits里面的data。然后我們繼續(xù)p *$2打印$2 = 0x0000000101104f50地址里面存儲什么數(shù)據(jù),發(fā)現(xiàn)看不懂,但是我們看到$2的數(shù)據(jù)類型是class_rw_t,我們在源碼項目里全局搜索下,發(fā)現(xià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 {
        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 {
        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};
        }
    }
};

我們把目光放在倒數(shù)最后三個函數(shù)聲明,發(fā)現(xiàn)很眼熟,method_array_t methods() property_array_t properties() protocol_array_t protocols(),這不就是方法數(shù)組,屬性數(shù)組,協(xié)議數(shù)組嘛,接下來我們就對$2調(diào)用method_array_t methods() property_array_t properties()兩個函數(shù)試試。

image.png

我們看到輸出了property_array_t結構的數(shù)據(jù),里面有個list,我們繼續(xù)打印list看看。
image.png

發(fā)現(xiàn)他是一個指針,我們打印下指針指向的數(shù)據(jù)看看。
image.png

OK,我們看到了LGFather的屬性name,果然如我們猜測,類成員屬性放在了bits里面,我們?nèi)缁鹋谥频脑倏纯创蛴?code>methods()。
image.png

發(fā)現(xiàn)實例方法真的也保存在bits里面,我們看到方法數(shù)組里面count為4,第一個方法為sayHi,那我們繼續(xù)打印第2,3,4個方法看看能否找到類方法sayGoodbye。
image.png

我們發(fā)現(xiàn)除了屬性nameget set方法外沒有其他方法了,我們的sayGoodbye類方法存放到哪里呢?欲知后事如何,請聽下回分解。

結論

萬物皆對象:類的本質就是對象
類在 class_rw_t 結構中存儲了編譯時確定的屬性、方法和協(xié)議等內(nèi)容。
實例方法存放在類中

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容