1. 類的初探
在isa結(jié)構(gòu)解析中,自定義LSPerson
類繼承自NSObject
,重寫成C++代碼如下
struct LSPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
};
struct NSObject_IMPL
結(jié)構(gòu)體定義如下
struct NSObject_IMPL {
Class isa;
};
typedef struct objc_class *Class;
Class為指向 結(jié)構(gòu)體struct objc_class
的指針佛嬉,所以繼承自NSObject
的對(duì)象中都有isa成員變量。那么NSObject
的isa來自何處呢定页?
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();
}
...省略的函數(shù)
}
由此可以知道struct objc_class
繼承自struct objc_object
struct objc_object
源碼如下
struct objc_object {
private:
isa_t isa;
public:
...省略的函數(shù)
}
總結(jié):在OC層梦湘,對(duì)象的isa指向?qū)ο蟮念愃澳龋瑢?duì)象的類繼承自NSObject
魁兼,NSObject
是以結(jié)構(gòu)體struct objc_class
為模版創(chuàng)建的婉徘;在C++層,struct objc_class
繼承自struct objc_object
咐汞,struct objc_object
內(nèi)部有isa成員變量盖呼。
2. 類的結(jié)構(gòu)分析
由結(jié)構(gòu)體struct objc_class
源碼可知,類的信息存儲(chǔ)在class_data_bits_t bits;
中化撕。
為了取到class_data_bits_t bits;
几晤,需要在結(jié)構(gòu)體struct objc_class
進(jìn)行地址偏移計(jì)算。
- Class ISA; //結(jié)構(gòu)體指針類型 8字節(jié)
- Class superclass;//結(jié)構(gòu)體指針類型 8字節(jié)
- cache_t 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_t
結(jié)構(gòu)體計(jì)算:explicit_atomic<uintptr_t> _maskAndBuckets;
long類型 8字節(jié)植阴; mask_t _mask_unused;
int類型 4字節(jié)蟹瘾; uint16_t _flags;
short類型 2字節(jié);uint16_t _occupied;
short類型 2字節(jié)掠手。根據(jù)內(nèi)存對(duì)齊原則cache_t
大小為16字節(jié)热芹。故為了取到class_data_bits_t bits;
,需要在結(jié)構(gòu)體struct objc_class
對(duì)首地址進(jìn)行偏移32字節(jié)惨撇。
探索流程:
//獲取LSPerson類首地址
(lldb) p/x LSPerson.class
(Class) $0 = 0x00000001000021c8 LSPerson
//獲取類isa中對(duì)內(nèi)存信息
(lldb) x/4gx 0x00000001000021c8
(lldb) x/4gx 0x00000001000021c8
0x1000021c8: 0x00000001000021a0 0x00007fff944ae118
0x1000021d8: 0x00007fff6cd18140 0x0000801000000000
//根據(jù)地址偏移0x1000021c8 -> 0x1000021d8 獲取bits信息
(lldb) p (class_data_bits_t *)0x1000021d8
(class_data_bits_t *) $1 = 0x00000001000021e8
//根據(jù)struct objc_class中data()函數(shù)獲取class_rw_t
(lldb) p $1->data()
(class_rw_t *) $2 = 0x00000001010224b0
(lldb) p *($2)
(class_rw_t) $4 = {
flags = 2148007936
witness = 0
ro_or_rw_ext = {
std::__1::atomic<unsigned long> = 4294975744
}
firstSubclass = nil
nextSiblingClass = NSUUID
}
(lldb) p $4.methods() //打印出對(duì)象方法列表
(lldb) p $4.properties() //打印出屬性列表
(lldb) p $4.protocols() //打印出協(xié)議列表
class_rw_t源碼
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:
...省去其他函數(shù)
public:
...省去其他函數(shù)
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 *>();
}
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};
}
}
};
根據(jù)已獲取的class_rw_t,調(diào)用結(jié)構(gòu)體內(nèi)部函數(shù)methods()
府寒、properties()
魁衙、protocols()
即可獲取類的方法列表报腔,屬性列表以及協(xié)議列表。