類(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的。那Class的isa右指向哪里呢。

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

從圖中可以到兩個(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指向哪里呢。

從上面看到對(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的指針地址和上面最后的根元類(lèi)的isa指針地址相同。由此可見(jiàn)內(nèi)存中NSObject和isa最終指向的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)對(duì)象在內(nèi)存中只存在一份,那么根元類(lèi)NSObject同樣在內(nèi)存中只存在一個(gè)份其isa指向自己。


實(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)的文章中使用Clang將main.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

內(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ì)象。

類(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é)。 -
superclass:Class類(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;
_buckets是struct bucket_t *類(lèi)型,是結(jié)構(gòu)體指針,占8字節(jié)。_mask的類(lèi)型是mask_t,是unsigned int類(lèi)型,占4字節(jié)。_maskAndBuckets是uintptr_t,是unsigned long,64位占8字節(jié),32位占4字節(jié)。使用注意所處的環(huán)境。_mask_unused是mask_t類(lèi)型,unsigned int類(lèi)型,占4字節(jié)。_flags與_occupied是uint16_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)需要的的列表


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

方法列表中有一個(gè)c++的析構(gòu)函數(shù)和實(shí)例方法和屬性的set和get方法。但是卻沒(méi)有類(lèi)方法。
通過(guò)結(jié)果發(fā)現(xiàn),屬性、實(shí)例方式其實(shí)是存儲(chǔ)在類(lèi)當(dāng)中的。
那么類(lèi)方法是否存在其元類(lèi)當(dāng)中呢?成員變量有存儲(chǔ)在哪里?下篇文章在探索。