前言:父类是子类的第一个成员变量,子类就拥有了父类的所有成员变量,叫结构体的伪继承
Class底层
Class是objc_class结构体指针,是objc_class的别名。
typedef struct objc_class *Class;
objc_class伪继承于objc_object,因此包含了isa,此外还有superclass、cache、bits几个成员变量,分析结构体内存,只需计算其成员变量大小。
struct objc_class : objc_object {objc_class(const objc_class&) = delete;objc_class(objc_class&&) = delete;void operator=(const objc_class&) = delete;void operator=(objc_class&&) = delete;// Class ISA;Class superclass;cache_t cache; // formerly cache pointer and vtableclass_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
isa
元类分析
首先,我们建立一个person类,断点调试,通过lldb得到p的指针地址0x108cc2650
//mask 0x00007ffffffffff8LGPerson *p = [LGPerson alloc];(lldb) x p0x108cc2650: 65 83 00 00 01 80 1d 01 00 00 00 00 00 00 00 00 e...............0x108cc2660: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
然后lldb命令x/4gx得到它的类内存结构,第一个为isa的地址0x011d800100008365。通过OC本质探索一文,我们可以通过isa & mask得到类地址0x0000000100008360,po得到了person类
(lldb) x/4gx 0x108cc26500x108cc2650: 0x011d800100008365 0x00000000000000000x108cc2660: 0x0000000000000000 0x0000000000000000(lldb) p/x 0x011d800100008365 & 0x00007ffffffffff8(long) $13 = 0x0000000100008360(lldb) po 0x0000000100008360LGPerson
此时,我们再通过x/4gx 类地址0x0000000100008360,又得到一个新的类内存结构,po 其isa & mask竟然也得到了Person类,其地址是0x0000000100008338,这个类地址和上面的0x0000000100008360哪一个是真正的person类地址呢?
通过打印类地址,确定person类,其地址为0x100008360
那另外一个地址是什么?我们可以通过MachOView分析Mach-o文件,首先确定了person类地址为0x100008360
在符号表中,我们找到了内存地址为0x0000000100008338的类,它是_objc_metaClass_LGPerson类型,是一个元类,由person类的isa指向,元类是由系统编译时自动生成的。
根元类分析
继续上面的逻辑,p/x 元类isa & mask,po 元类isa & mask得到的了NSObject类,所以person元类的isa指向了NSObject,这里NSObject的isa与superClass地址相同,都是0x00007fff8897eca0
(lldb) x/4gx 0x00000001000083380x100008338: 0x00007fff8897eca0 0x00007fff8897eca00x100008348: 0x0000000108cc2670 0x0002e03500000003(lldb) p/x 0x00007fff8897eca0 & 0x00007ffffffffff8(long) $28 = 0x00007fff8897eca0(lldb) po 0x00007fff8897eca0NSObject
重复操作上面逻辑,观察NSObject类的isa指向了自己,这时执行p/x NSObject.class和x/4gx,发现其isa地址同样为0x00007fff8897eca0,所以这里NSObject为根元类
(lldb) x/4gx 0x00007fff8897eca00x7fff8897eca0: 0x00007fff8897eca0 0x00007fff8897ecc80x7fff8897ecb0: 0x0000000108b06f60 0x0005e03100000007(lldb) p/x 0x00007fff8897eca0 & 0x00007ffffffffff8(long) $30 = 0x00007fff8897eca0(lldb) po 0x00007fff8897eca0NSObject(lldb) p/x NSObject.class(Class) $32 = 0x00007fff8897ecc8 NSObject(lldb) x/4gx 0x00007fff8897ecc80x7fff8897ecc8: 0x00007fff8897eca0 0x00000000000000000x7fff8897ecd8: 0x0000000108cbc610 0x0001801000000003
isa流程
superClass
通过以下代码打印,得到superClass的对应关系
void lgTestNSObject(void){// NSObject实例对象NSObject *object1 = [NSObject alloc];// NSObject类Class class = object_getClass(object1);// NSObject元类Class metaClass = object_getClass(class);// NSObject根元类Class rootMetaClass = object_getClass(metaClass);// NSObject根根元类Class rootRootMetaClass = object_getClass(rootMetaClass);NSLog(@"\n%p 实例对象\n%p 类\n%p 元类\n%p 根元类\n%p 根根元类",object1,class,metaClass,rootMetaClass,rootRootMetaClass);// LGPerson元类Class pMetaClass = object_getClass(LGPerson.class);Class psuperClass = class_getSuperclass(pMetaClass);NSLog(@"%@ - %p",psuperClass,psuperClass);// LGTeacher -> LGPerson -> NSObject// 元类也有一条继承链Class tMetaClass = object_getClass(LGTeacher.class);Class tsuperClass = class_getSuperclass(tMetaClass);NSLog(@"%@ - %p",tsuperClass,tsuperClass);// NSObject 根类特殊情况Class nsuperClass = class_getSuperclass(NSObject.class);NSLog(@"%@ - %p",nsuperClass,nsuperClass);// 根元类 -> NSObjectClass rnsuperClass = class_getSuperclass(metaClass);NSLog(@"%@ - %p",rnsuperClass,rnsuperClass);}0x10893d4c0 实例对象0x7fff8897ecc8 类0x7fff8897eca0 元类0x7fff8897eca0 根元类0x7fff8897eca0 根根元类NSObject - 0x7fff8897eca0LGPerson - 0x100008338(null) - 0x0NSObject - 0x7fff8897ecc8
superClass流程
cache
cache_t同样是结构体,其大小与变量有关
_bucketsAndMaybeMask是uintptr_t类型,就是unsignedlong,大小为8字节union为联合体,取内存较大成员变量_maybeMask是mask_t(uint32_t)类型,占4字节,_flags为uint16_t,占2字节,_occupied为uint16_t,占2字节- _originalPreoptCache为结构体指针,占8个字节
因此cache大小为16字节
struct cache_t {private:explicit_atomic<uintptr_t> _bucketsAndMaybeMask;union {struct {explicit_atomic<mask_t> _maybeMask;#if __LP64__uint16_t _flags;#endifuint16_t _occupied;};explicit_atomic<preopt_cache_t *> _originalPreoptCache;};
bits
bits在底层是class_data_bits_t结构体,而class_data_bits_t关键就是返回class_rw_t指针 ``` struct class_data_bits_t { friend objc_class;// Values are the FAST_ flags above. uintptr_t bits; private: …
public:
class_rw_t* data() const {return (class_rw_t *)(bits & FAST_DATA_MASK);}...
}
`class_rw_t`同样是一个结构体,其包含了`ro、method_array_t、property_array_t、protocol_array_t`方法调用<br /><br />我们可以通过lldb调试验证下bits中包含的属性,在类结构体中(方法存储在方法区,不占用结构体内存)- 第一成员变量`isa`,占8字节- 第二成员变量`superClass`,占8字节- 第三成员变量`cache`,占16字节因此,第四成员变量`bits`地址,可以通过类地址 + 32得到
(lldb) p/x LGPerson.class (Class) $3 = 0x0000000100008380 LGPerson (lldb) p (class_data_bits_t)(0x0000000100008380 + 32) (class_data_bits_t) $4 = (bits = 4441863924)
得到class_data_bits_t后,可以依次取出其中的property,method,ivars
(lldb) p $4.data()
(class_rw_t ) $5 = 0x0000000108c176f0
(lldb) p ($5).properties()
(const property_array_t) $6 = {
list_array_tt
```(lldb) p/x LGPerson.class(Class) $0 = 0x0000000100008380 LGPerson(lldb) p *(class_data_bits_t*)(0x0000000100008380 + 32)(class_data_bits_t) $1 = (bits = 4443234692)(lldb) p $1.data()(class_rw_t *) $2 = 0x0000000108d66180(lldb) p *$2(class_rw_t) $3 = {flags = 2148007936witness = 1ro_or_rw_ext = {std::__1::atomic<unsigned long> = {Value = 4295000344}}firstSubclass = nilnextSiblingClass = NSUUID}(lldb) p $3.methods()(const method_array_t) $4 = {list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {= {list = {ptr = 0x0000000100008160}arrayAndFlag = 4295000416}}}(lldb) p $4.list.ptr(method_list_t *const) $5 = 0x0000000100008160(lldb) p *$5(method_list_t) $6 = {entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 6)}(lldb) p $6.get(0).big(method_t::big) $7 = {name = "hobby"types = 0x0000000100003f53 "@16@0:8"imp = 0x0000000100003dc0 (KCObjcBuild`-[LGPerson hobby])}Fix-it applied, fixed expression was:$6.get(0).big()(lldb) p $6.get(1).big(method_t::big) $8 = {name = "sayNB"types = 0x0000000100003f5b "v16@0:8"imp = 0x0000000100003d50 (KCObjcBuild`-[LGPerson sayNB])}Fix-it applied, fixed expression was:$6.get(1).big()(lldb) p $6.get(2).big(method_t::big) $9 = {name = "setHobby:"types = 0x0000000100003f6f "v24@0:8@16"imp = 0x0000000100003df0 (KCObjcBuild`-[LGPerson setHobby:])}Fix-it applied, fixed expression was:$6.get(2).big()(lldb) p $6.get(3).big(method_t::big) $10 = {name = "init"types = 0x0000000100003f53 "@16@0:8"imp = 0x0000000100003cf0 (KCObjcBuild`-[LGPerson init])}Fix-it applied, fixed expression was:$6.get(3).big()(lldb) p $6.get(4).big(method_t::big) $11 = {name = "name"types = 0x0000000100003f53 "@16@0:8"imp = 0x0000000100003d60 (KCObjcBuild`-[LGPerson name])}Fix-it applied, fixed expression was:$6.get(4).big()(lldb) p $6.get(5).big(method_t::big) $12 = {name = "setName:"types = 0x0000000100003f6f "v24@0:8@16"imp = 0x0000000100003d90 (KCObjcBuild`-[LGPerson setName:])}Fix-it applied, fixed expression was:$6.get(5).big()
(lldb) p/x LGPerson.class(Class) $0 = 0x0000000100008380 LGPerson(lldb) p *(class_data_bits_t *)(0x0000000100008380 + 32)(class_data_bits_t) $1 = (bits = 4441981524)(lldb) p $1.data()(class_rw_t *) $2 = 0x0000000108c34250(lldb) p (*$2).ro()(const class_ro_t *) $3 = 0x0000000100008118(lldb) p (*$3).ivars(const ivar_list_t *const) $4 = 0x00000001000081f8(lldb) p *$4(const ivar_list_t) $5 = {entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 3)}(lldb) p $5.get(0)(ivar_t) $6 = {offset = 0x0000000100008318name = 0x0000000100003f19 "subject"type = 0x0000000100003f63 "@\"NSString\""alignment_raw = 3size = 8}(lldb) p $5.get(1)(ivar_t) $7 = {offset = 0x0000000100008320name = 0x0000000100003f21 "_name"type = 0x0000000100003f63 "@\"NSString\""alignment_raw = 3size = 8}(lldb) p $5.get(2)(ivar_t) $8 = {offset = 0x0000000100008328name = 0x0000000100003f27 "_hobby"type = 0x0000000100003f63 "@\"NSString\""alignment_raw = 3size = 8}
总结
- class内存结构

