一、ISA分析到元类
LGPerson *p = [[LGPerson alloc] init];
Class class1= [LGPerson class];
Class class2= p.class;
Class class3= object_getClass(p);
Class class4= object_getClass([LGPerson alloc]);
NSLog(@"\n%p-\n%p-\n%p-\n%p-",class1,class2,class3,class4);
Class class5= object_getClass(class3);
NSLog(@"\n%p",class5);
输出结果:
0x100dc54d0-
0x100dc54d0-
0x100dc54d0-
0x100dc54d0-
0x100dc54a8
(lldb) po 0x100dc54d0
LGPerson
(lldb) po 0x100dc54a8
LGPerson
总结:
1、类对象的地址都是一样的,内存中每一个类只有一块内存
2、LGPerson
类有两个不一样
的地址,但是一个类对象只有一个地址。0x100dc54d0
是LGPerson
的类地址,那么0x100dc54a8
这个类地址苹果把它叫做元类
结合lldb
以及isa
和mask
进行探究:
-
实例对象
的isa
-->类
-
类
的isa
-->元类
-
元类
的isa
-->根元类
(NSObject的isa) -
根元类
的isa
-->根元类
(指向自己)
二、ISA走位图和继承链
NSLog(@"*********************************************");
NSLog(@"\n实例对象地址 - %p",objc);
NSLog(@"\n类对象地址 - %p",[objc class]);
NSLog(@"\n父类对象地址 - %p",[objc superclass]);
NSLog(@"\n元类对象地址 - %p",object_getClass([objc class]));
NSLog(@"\n元类的父类对象地址 - %p",[object_getClass([objc class]) superclass]);
NSLog(@"\n根元类对象地址 - %p",object_getClass(object_getClass([objc class])));
NSLog(@"\n根元类的父类对象地址 - %p",[object_getClass(object_getClass([objc class])) superclass]);
NSLog(@"\n根根元类对象地址 - %p",object_getClass(object_getClass(object_getClass([objc class]))));
NSLog(@"\n根根元类的父类对象地址 - %p",[object_getClass(object_getClass(object_getClass([objc class]))) superclass]);
NSLog(@"*********************************************");
结果以及lldb调试:
*********************************************
实例对象地址 - 0x281476400 // Student的实例对象地址
类对象地址 - 0x1025d55b8 // Student的类对象地址
父类对象地址 - 0x1025d5568 // Student的父类对象是Person的类对象
元类对象地址 - 0x1025d5590 // Student的元类对象地址
元类的父类对象地址 - 0x1025d5540 // Person的元类对象地址
根元类对象地址 - 0x1db235de0 // NSObject元类对象地址
根元类的父类对象地址 - 0x1db235e08 // NSObject 类对象地址
根根元类对象地址 - 0x1db235de0 // NSObject元类对象地址
根根元类的父类对象地址 - 0x1db235e08// NSObject 类对象地址
*********************************************
(lldb) p/x [LGStudent class] // Student的类对象地址
(Class) $0 = 0x00000001025d55b8 LGStudent
(lldb) p/x [LGPerson class] // Person的类对象地址
(Class) $1 = 0x00000001025d5568 LGPerson
(lldb) p/x [NSObject class] //NSObject的类对象地址
(Class) $2 = 0x00000001db235e08 NSObject
(lldb) p/x $0.superclass // student的父类是person
(Class) $3 = 0x00000001025d5568 LGPerson
(lldb) p/x $1.superclass // person的父类是NSObject
(Class) $4 = 0x00000001db235e08 NSObject
(lldb) p/x $2.superclass // NSOject的父类是nil
(Class) $5 = nil
(lldb) p/x object_getClass($0)// Student元类
(Class _Nullable) $6 = 0x00000001025d5590
(lldb) p/x object_getClass($1)// Person元类
(Class _Nullable) $7 = 0x00000001025d5540
(lldb) p/x object_getClass($2)// NSObject元类
(Class _Nullable) $8 = 0x00000001db235de0
(lldb) p/x $6.superclass// Student元类的父类是 Person元类
(Class) $9 = 0x00000001025d5540
(lldb) p/x $7.superclass// Person元类 的父类 NSObject元类
(Class) $10 = 0x00000001db235de0
(lldb) p/x $8.superclass// Person元类 的父类 NSObject类对象
(Class) $11 = 0x00000001db235e08 NSObject
(lldb) p/x object_getClass($8)// Person元类 的元类 是其本身
(Class _Nullable) $12 = 0x00000001db235de0
结论:
三、分析类的结构
3.1 源码分析类的结构
object2.0(基于objc4(818版本)的源码)
源码位置为objc-runtime-new.h文件
struct objc_class : objc_object {
...
// Class ISA; //8字节
Class superclass; // 8字节
cache_t cache; //16字节 // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
... 省略大部分源码
}
源码分析objc_class
继承objc_object
,objc_object
里面只有一个成员变量isa
,isa
具体是作用已经探究过。
可以通过首地址
+偏移值
来获取里面的成员变量的地址,然后获取值去探究这三个成员变量的具体作用,但是偏移值
需要知道当前变量之前的所有成员变量的大小
-
isa
结构体指针占8字节
-
Class superclass
也是结构体指针占8字节
-
cache_t cache
是结构体类型的大小,由结构体内成员变量决定 -
class_data_bits_t bits
知道前面三个成员变量大小,就可以得到bits
的地址
只要知道cache_t
的内存大小,objc_class
的所有的成员变量都可以获取到相应的地址,探究下cache_t
内存大小:16字节
struct cache_t {
private:
explicit_atomic<uintptr_t> _bucketsAndMaybeMask; // 8
union { // 联合体大小 8
struct {
explicit_atomic<mask_t> _maybeMask; // 4
#if __LP64__
uint16_t _flags; // 2
#endif
uint16_t _occupied; // 2
};
explicit_atomic<preopt_cache_t *> _originalPreoptCache;//8
};
...
}
cache_t
是结构体类型,有两个成员变量_bucketsAndMaybeMask
和一个联合体
_bucketsAndMaybeMask
是uintptr_t
无符长整型占8字节
- 联合体里面有两个成员变量
结构体
和_originalPreoptCache
,联合体的内存大小由成员变量中的最大变量类型决定 _originalPreoptCache
是结构体指针占8字节
- 结构体中有
_maybeMask
,_flags
,_occupied
。_maybeMask
是uint32_t
占4字节
,_flags
和_occupied
是uint16_t
各占2字节
,结构体大小是8字节
cache_t
的内存大小是8
+8
或者是8
+4
+2
+2
都是16
字节
探究类里面的各个成员变量,成员变量的内存地址如下
isa
的内存地址是首地址
superclass
的内存地址是首地址
+0x8
cache
的内存地址是首地址
+0x10
bits
的内存地址是首地址
+0x20
3.2 分析properties()
分析属性列表, class_data_bits_t -> class_rw_t -> properties()
(lldb) x/4gx LGPerson.class
0x100008380: 0x00000001000083a8 0x000000010036a140
0x100008390: 0x0000000100619e90 0x0002802800000003
(lldb) p/x 0x100008380+0x20 // 结构体指针偏移到 bits 的位置, 8(ISA) + 8(superclass) + 16(cache)
(long) $1 = 0x00000001000083a0
(lldb) p (class_data_bits_t *)$1
(class_data_bits_t *) $2 = 0x00000001000083a0
(lldb) p $2->data()
(class_rw_t *) $3 = 0x0000000100619ce0
(lldb) p $3.properties
(const property_array_t) $4 = {
list_array_tt<property_t, property_list_t, RawPtr> = {
= {
list = {
ptr = 0x0000000100008260
}
arrayAndFlag = 4295000672
}
}
}
Fix-it applied, fixed expression was:
$3->properties()
(lldb) p $4.list.ptr
(property_list_t *const) $5 = 0x0000000100008260
(lldb) p *$5
(property_list_t) $6 = {
entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2)
}
(lldb) p $6.get(0)
(property_t) $7 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
(lldb) p $6.get(1)
(property_t) $8 = (name = "hobby", attributes = "T@\"NSString\",C,N,V_hobby")
(lldb) p $6.get(2)
Assertion failed: (i < count), function get... // 越界
3.3 分析methods()
分析方法列表, class_data_bits_t -> class_rw_t -> methods():
(lldb) p/x LGPerson.class
(Class) $0 = 0x0000000100008380 LGPerson
(lldb) p/x 0x0000000100008380+0x20
(long) $1 = 0x00000001000083a0
(lldb) p (class_data_bits_t *)$1
(class_data_bits_t *) $2 = 0x00000001000083a0
(lldb) p $2->data()
(class_rw_t *) $3 = 0x000000010079a710
(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
}
}
}
Fix-it applied, fixed expression was:
$3->methods()
(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 = "sayNB"
types = 0x0000000100003f77 "v16@0:8"
imp = 0x0000000100003d40 (KCObjcBuild`-[LGPerson sayNB])
}
(lldb) p $6.get(1).big()
(method_t::big) $8 = {
name = "hobby"
types = 0x0000000100003f6f "@16@0:8"
imp = 0x0000000100003db0 (KCObjcBuild`-[LGPerson hobby])
}
(lldb) p $6.get(2).big()
(method_t::big) $9 = {
name = "setHobby:"
types = 0x0000000100003f8b "v24@0:8@16"
imp = 0x0000000100003de0 (KCObjcBuild`-[LGPerson setHobby:])
}
(lldb) p $6.get(3).big()
(method_t::big) $10 = {
name = "init"
types = 0x0000000100003f6f "@16@0:8"
imp = 0x0000000100003ce0 (KCObjcBuild`-[LGPerson init])
}
(lldb) p $6.get(4).big()
(method_t::big) $11 = {
name = "name"
types = 0x0000000100003f6f "@16@0:8"
imp = 0x0000000100003d50 (KCObjcBuild`-[LGPerson name])
}
(lldb) p $6.get(5).big()
(method_t::big) $12 = {
name = "setName:"
types = 0x0000000100003f8b "v24@0:8@16"
imp = 0x0000000100003d80 (KCObjcBuild`-[LGPerson setName:])
}
(lldb) p $6.get(6).big()
Assertion failed: (i < count), function get... //越界
- 1、属性的get和设置方法也在里面
- 2、只声明未实现的方法不会展示
- 3、发现类方法不在该处,那是不是在元类里面呢,下面观察一下
(lldb) p object_getClass(LGPerson.class)// 得到元类的内存地址
(Class) $0 = 0x00000001000083a8
(lldb) p/x 0x00000001000083a8+0x20
(long) $1 = 0x00000001000083c8
(lldb) p (class_data_bits_t *)$1
(class_data_bits_t *) $2 = 0x00000001000083c8
(lldb) p $2->data()
(class_rw_t *) $3 = 0x0000000100679810
(lldb) p $3->methods()
(const method_array_t) $4 = {
list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
= {
list = {
ptr = 0x00000001000082d0
}
arrayAndFlag = 4295000784
}
}
}
(lldb) p $4.list.ptr
(method_list_t *const) $5 = 0x00000001000082d0
(lldb) p *$5
(method_list_t) $6 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 1)
}
(lldb) p $6.get(0).big()
(method_t::big) $7 = {
name = "sayHello_MyClassMethod"
types = 0x0000000100003f77 "v16@0:8"
imp = 0x0000000100003e10 (KCObjcBuild`+[LGPerson sayHello_MyClassMethod])
}
此时, 我们在元类中找到了类方法 sayHello_MyClassMethod
3.4 分析 ro()->ivars
分析成员列表, class_data_bits_t -> class_rw_t -> class_ro_t -> ivars:
(lldb) p/x LGPerson.class
(Class) $0 = 0x0000000100008380 LGPerson
(lldb) p/x 0x0000000100008380+0x20
(long) $1 = 0x00000001000083a0
(lldb) p (class_data_bits_t *)$1
(class_data_bits_t *) $2 = 0x00000001000083a0
(lldb) p $2->data()
(class_rw_t *) $3 = 0x000000010071dc70
(lldb) p $3->ro()
(const class_ro_t *) $4 = 0x0000000100008118
(lldb) p $4->ivars
(const ivar_list_t *const) $5 = 0x00000001000081f8
(lldb) p *$5
(const ivar_list_t) $6 = {
entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 3)
}
(lldb) p $6.get(0)
(ivar_t) $7 = {
offset = 0x0000000100008318
name = 0x0000000100003f1f "_myVar"
type = 0x0000000100003f7e "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $6.get(1)
(ivar_t) $8 = {
offset = 0x0000000100008320
name = 0x0000000100003f26 "_name"
type = 0x0000000100003f7e "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $6.get(2)
(ivar_t) $9 = {
offset = 0x0000000100008328
name = 0x0000000100003f2c "_hobby"
type = 0x0000000100003f7e "@\"NSString\""
alignment_raw = 3
size = 8
}
类的结构图
最后我们画一下类的结构图