類(lèi)的分析、探索

類(lèi)的isa探究

  • 準(zhǔn)備研究類(lèi)LGPerson類(lèi)。沿用探究oc對(duì)象的思路,使用lldb先從類(lèi)的內(nèi)存、isa來(lái)進(jìn)行觀察。
    我們已經(jīng)知道對(duì)象isa是指向其Class的。那Classisa右指向哪里呢。
類(lèi)內(nèi)存及指針地址

從結(jié)果發(fā)現(xiàn)LGPerson類(lèi)的isa指向的是LGPerson自己。我們知道LGPerson的實(shí)例對(duì)象personisa也是指向LGPerson的。那么兩個(gè)isa的地址應(yīng)該是指向了同一塊內(nèi)存,那么兩個(gè)isa地址是否相同呢。

LGPerson & person

從圖中可以到兩個(gè)isa的地址是不相同。但是其兩個(gè)isa都指向了LGPerson。

  • person對(duì)象中,其isa的指針地址& ISA_MASK后得到的是person的類(lèi)LGPerson。
  • LGPerson類(lèi)中,其isa的指針地址& ISA_MASK后得到的是LGPerson類(lèi)的類(lèi)。
  • Apple中,稱(chēng)LGPerson類(lèi)的類(lèi)為元類(lèi)。
元類(lèi)
  • 對(duì)象isa指向類(lèi)類(lèi)其實(shí)也是一個(gè)對(duì)象。可稱(chēng)之為類(lèi)對(duì)象,其isa指向apple定義的元類(lèi)。
  • 元類(lèi)系統(tǒng)給的,其定義、創(chuàng)建都由編譯器自動(dòng)完成,類(lèi)的歸屬來(lái)自于元類(lèi)
  • 元類(lèi)類(lèi)對(duì)象類(lèi),每個(gè)類(lèi)都有一個(gè)獨(dú)一無(wú)二的的元類(lèi)用來(lái)存儲(chǔ)類(lèi)的相關(guān)信息。
  • 元類(lèi)本身是沒(méi)有名稱(chēng)的,由于其與類(lèi)相關(guān),所以使用了同類(lèi)名相同的名稱(chēng)。

那么按照上面邏輯繼續(xù)下去LGPerson類(lèi)類(lèi)的isa指向哪里呢。

isa指向

從上面看到對(duì)象的isa指向類(lèi),LGPerson 類(lèi)對(duì)象isa指向元類(lèi),都是LGPerson,最終isa最終指向了一個(gè)NSObject類(lèi)。

  • 那么這個(gè)NSObject和我們內(nèi)存中NSObject一樣嗎。
  • 類(lèi)對(duì)象內(nèi)存中又存在多少呢。

查看NSObject的信息

NSObject類(lèi)的isa

NSObject類(lèi)isa的指針地址和上面最后的根元類(lèi)的isa指針地址相同。由此可見(jiàn)內(nèi)存中NSObjectisa最終指向的NSObject應(yīng)該是同一個(gè)。

創(chuàng)建多個(gè)類(lèi)對(duì)象輸出內(nèi)存地址查看。

 Class class1 = [LGPerson class];
 Class class2 = [LGPerson alloc].class;
 Class class3 = object_getClass([LGPerson alloc]);
 NSLog(@"\n%p-\n%p-\n%p-\n%p", class1, class2, class3);
類(lèi)地址

可以看出其地址是完全相同的,因此可以得出類(lèi)對(duì)象內(nèi)存中只存在一份,那么根元類(lèi)NSObject同樣在內(nèi)存中只存在一個(gè)份其isa指向自己。

isa指向
isa流程圖.png
  • 實(shí)例對(duì)象(Instance of Subclass)isa指向類(lèi)(class)
  • 類(lèi)對(duì)象(class)isa指向元類(lèi)(metal class)
  • 元類(lèi)(metal class)isa指向根元類(lèi)(root metal class)
  • 根元類(lèi)(root metal class)isa指向其自己

注意:實(shí)例對(duì)象之間沒(méi)有繼承關(guān)系。類(lèi)之間才存在繼承關(guān)系。NSObject繼承自nil。

類(lèi)結(jié)構(gòu)分析

在探究isa與cls關(guān)聯(lián)的文章中使用Clangmain.m轉(zhuǎn)化成c++文件探究對(duì)象的本質(zhì)。同樣用此去探究類(lèi)Class的是什么。

typedef struct objc_class *Class;

c++文件可以發(fā)現(xiàn)Class是一個(gè)objc_class的結(jié)構(gòu)體。接下來(lái)進(jìn)入apple提供的objc4源碼中探究Class。

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)被廢除。尋找新的定義objc-runtime-new.h

新objc_class定義

內(nèi)容太長(zhǎng)了,只截了少部分objc4-781源碼
在源碼中搜索發(fā)現(xiàn)objc_object會(huì)發(fā)也有兩個(gè)版本

//位于objc.h文件
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
//位于objc-private.h文件
struct objc_object {
private:
    isa_t isa;

public:

    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();
    ....
}

結(jié)合編譯的c++main.cpp的文件中顯示,使用objc.h文件中定義的版本。

struct objc_object {
    Class _Nonnull isa __attribute__((deprecated));
};

結(jié)合objc4源碼和main.cpp底層編譯發(fā)現(xiàn):

  • objc_class繼承自objc_object。objc_object結(jié)構(gòu)體,擁有isa,所以
    objc_class也有isa

  • NSObject中的isa是由Class定義,Class來(lái)自objc_class,所以NSObject同樣也有isa。

  • NSObject實(shí)例化一個(gè)對(duì)象objc,因繼承關(guān)系objc滿足objc_object的特性(擁有isa)。isa表示指向,來(lái)源于objc_object

  • 所有的對(duì)象都是以objc_object為模板繼承過(guò)來(lái)的。所以所有對(duì)象都擁有objc_object的特性。objc_object是當(dāng)前的根對(duì)象。

objc_object & objc_class & object & NSObject & isa

類(lèi)結(jié)構(gòu)分析、屬性列表、方法列表探索

@interface LGPerson : NSObject
{
    NSString *oldName;
}
@property (copy, nonatomic) NSString *nickName;
-(void)sayHello;
+(void)sayNo;
@end

結(jié)構(gòu)分析

struct objc_class : objc_object {
    // Class ISA;//8字節(jié)
    Class superclass;//8字節(jié)
    cache_t cache; //16字節(jié)            // 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();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }
    ......//方法未全部貼出
}
  • isa:雖然顯示注釋掉了,但是其繼承自objc_object,所以是有isa的。占8字節(jié)。
  • superclassClass類(lèi)型,由objc_object定義,是一個(gè)指針8字節(jié)。
  • cache:結(jié)構(gòu)體,內(nèi)存大小有其內(nèi)成員決定。結(jié)構(gòu)體指針8字節(jié)。

cache內(nèi)存大小計(jì)算
此處只粘貼了需要計(jì)算的部分。(用static修飾的不存在結(jié)構(gòu)體內(nèi)存中)

#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;
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
    // _maskAndBuckets stores the mask shift in the low 4 bits, and
    // the buckets pointer in the remainder of the value. The mask
    // shift is the value where (0xffff >> shift) produces the correct
    // mask. This is equal to 16 - log2(cache_size).
    explicit_atomic<uintptr_t> _maskAndBuckets;
    mask_t _mask_unused;
#else
#error Unknown cache mask storage type.
#endif

#if __LP64__
    uint16_t _flags;
#endif
    uint16_t _occupied;
  • _bucketsstruct bucket_t *類(lèi)型,是結(jié)構(gòu)體指針,占8字節(jié)。

  • _mask的類(lèi)型是mask_t,是unsigned int類(lèi)型,占4字節(jié)

  • _maskAndBucketsuintptr_t,是unsigned long,64位占8字節(jié),32位占4字節(jié)。使用注意所處的環(huán)境。

  • _mask_unusedmask_t類(lèi)型,unsigned int類(lèi)型,占4字節(jié)。

  • _flags與_occupieduint16_t類(lèi)型,是unsigned short,占2字節(jié)。

注意:cache計(jì)算內(nèi)存注意宏定義的if else并不是每一個(gè)都需要。此處使用的是64位所以其內(nèi)存大小8 + 4 + 2 +2 = 16

上面我們計(jì)算好了bits前面?zhèn)€成員所占字節(jié)。可以通過(guò)首地址平移的方法或bits屬性,查看屬性及方法的存儲(chǔ)。

bits中的數(shù)據(jù)可通過(guò)指針函數(shù)*data()獲取。

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();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }
    ......
}

bits中存儲(chǔ)的信息類(lèi)型是class_rw_t。在其源碼中會(huì)發(fā)現(xiàn)需要的的列表

class_rw_t中屬性、方法列表
屬性列表

從輸出的結(jié)果中可以看到LGPerson類(lèi)中bits中屬性列表中有nickName屬性,但是卻只有1一個(gè)nickName屬性,沒(méi)有成員變量oldName。

方法列表

方法列表中有一個(gè)c++析構(gòu)函數(shù)實(shí)例方法和屬性的setget方法。但是卻沒(méi)有類(lèi)方法

通過(guò)結(jié)果發(fā)現(xiàn),屬性、實(shí)例方式其實(shí)是存儲(chǔ)在類(lèi)當(dāng)中的。

那么類(lèi)方法是否存在其元類(lèi)當(dāng)中呢?成員變量有存儲(chǔ)在哪里?下篇文章在探索。

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

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