目录:
- 1:isa指向与元类的superclass指向分析
- 1.1 isa掩码(ISA_MASK)介绍
- 1.2 isa指向分析
- 1.3 元类superclass指向分析
- 2:类的结构分析
- 2.1 ObjC 1.0的objc_class(已弃用)
- 2.2 ObjC 2.0的objc_class(基于objc4-818.2)
- 2.2.1 objc-runtime-old.h文件里objc_class的定义
- 2.2.2 objc-runtime-new.h文件里objc_class的定义
- 2.3 objc_class成员变量bits的分析
- 2.3.1 内存平移
- 2.3.2 类的结构内存计算
- 2.3.3 获取类的属性
- 2.3.4 获取类的实例方法
- 2.3.5 获取类的成员变量(ivars)
- 2.3.6 获取类的类方法
- 2.3.7 获取类的协议
1:isa
指向与元类的superclass指向分析
书接上文OC底层原理初探之对象的本质(三)alloc探索下继续对类的原理进行探索。
1.1 isa
掩码(ISA_MASK
)介绍
上文OC底层原理初探之对象的本质(三)alloc探索下中已经介绍过ISA_MASK
及其使用方法,接下来我们的探索需要借助ISA_MASK
,现行源码版本一共分为三种架构:
x86_64: define ISA_MASK 0x00007ffffffffff8ULL // ULL: unsigned long long 无符号长整形
arm64: define ISA_MASK 0x0000000ffffffff8ULL
arm64(simulators): define ISA_MASK 0x007ffffffffffff8ULL
1.2 isa
指向分析
代码示例:
#import <Foundation/Foundation.h>
#import "XJPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
XJPerson *p = [XJPerson alloc];
NSLog(@"%@",p);
}
return 0;
}
********************** lldb调试结果 **********************
(lldb) x/4gx p // 16进制格式化输出对象p的内存信息
0x10044f160: 0x001d800100008365 0x0000000000000000 // 得到对象p的isa
0x10044f170: 0x0000000000000000 0x0000000000000000
(lldb) p/x 0x001d800100008365 & 0x00007ffffffffff8 // 用前文的方式,isa & ISA_MASK
(long) $1 = 0x0000000100008360 // 得到类的地址信息
(lldb) po 0x0000000100008360 // po 输出
XJPerson // 得到XJPerson
// 继续探索
(lldb) x/4gx 0x0000000100008360 // 16进制格式化输出XJPerson类的内存信息
0x100008360: 0x0000000100008338 0x00007fff932d8118 // 得到XJPerson类的isa
0x100008370: 0x00007fff6bbb6140 0x0000802c00000000
(lldb) p/x 0x0000000100008338 & 0x00007ffffffffff8 // isa & ISA_MASK
(long) $3 = 0x0000000100008338 // 得到一个新的地址
(lldb) po 0x0000000100008338 // po 输出
XJPerson // 还是可以得到XJPerson
很显然,两次输出XJPerson
的地址不一样,分别为0x0000000100008360
和0x0000000100008338
,这里不禁产生了一个疑问,难道类在运行时会类似对象一样创建多份吗?接下来验证一下:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "XJPerson.h"
//MARK: - 分析类对象内存存在个数
void xjTestClassNum(void){
Class class1 = [XJPerson class];
Class class2 = [XJPerson alloc].class;
Class class3 = object_getClass([XJPerson alloc]);
Class class4 = [XJPerson alloc].class;
NSLog(@"\n%p-\n%p-\n%p-\n%p",class1,class2,class3,class4);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
xjTestClassNum();
}
return 0;
}
********************** 打印输出结果 **********************
2021-06-19 15:39:55.257537+0800 002-isa分析[5557:148166]
0x100008360-
0x100008360-
0x100008360-
0x100008360
Program ended with exit code: 0
从上面案例可以看出,不管生成多少个对象,类始终只有一个,那么0x0000000100008338
这个地址po
输出为什么也是XJPerson
呢?下面用MachOView
读取运行文件看看究竟:
在MachOView
搜索class
除了能看到_OBJC_CLASS_$_XJPerson
外,还能看到_OBJC_METACLASS_$_XJPerson
,这就是元类(MetaClass)
。我们代码中并没有主动生成元类
,说明这是程序在运行时主动帮我们生成的。
到这里已经知道对象的isa
->类对象,类对象的isa
->元类(MetaClass),继续深入探索:
// 省略一些之前代码,并添加XJTeacher类的实例对象的创建代码,XJTeacher继承自XJPerson
// 实例化XJTeacher类的对象
XJTeacher *t = [XJTeacher alloc];
NSLog(@"%@",t);
********************** lldb调试结果 **********************
// XJPerson lldb 调试
// 省略一些之前XJPerson类lldb调试内容
(lldb) po 0x0000000100008338 // XJPerson类的元类
XJPerson
(lldb) x/4gx 0x0000000100008338 // 16进制格式化输出XJPerson元类的内存信息
0x100008338: 0x00007fff932d80f0 0x00007fff932d80f0 // 得到XJPerson元类的isa
0x100008348: 0x0000000100406c50 0x0003e03500000007
(lldb) p/x 0x00007fff932d80f0 & 0x00007ffffffffff8 // isa & ISA_MASK
(long) $5 = 0x00007fff932d80f0 // 得到新的地址
(lldb) po 0x00007fff932d80f0 // po 输出
NSObject // 输出为NSObject,那么这是类还是元类呢
// XJTeacher lldb 调试
(lldb) x/4gx t // 16进制格式化输出对象t的内存信息
0x100522330: 0x001d800100008315 0x0000000000000000 // 得到对象t的isa
0x100522340: 0x0000000000000000 0x0000000000000000
(lldb) p/x 0x001d800100008315 & 0x00007ffffffffff8 // isa & ISA_MASK
(long) $1 = 0x0000000100008310 // 得到新的地址
(lldb) po 0x0000000100008310 // po 输出
XJTeacher // 输出为XJTeacher,即类XJTeacher
(lldb) x/4gx 0x0000000100008310 // 16进制格式化输出类XJTeacher的内存信息
0x100008310: 0x00000001000082e8 0x0000000100008360 // 得到类XJTeacher的isa
0x100008320: 0x00007fff6bbb6140 0x0000802c00000000
(lldb) p/x 0x00000001000082e8 & 0x00007ffffffffff8 // isa & ISA_MASK
(long) $3 = 0x00000001000082e8 // 得到新的地址
(lldb) po 0x00000001000082e8 // po 输出
XJTeacher // 输出为XJTeacher,即元类XJTeacher
(lldb) x/4gx 0x00000001000082e8 // 16进制格式化输出元类XJTeacher的内存信息
0x1000082e8: 0x00007fff932d80f0 0x0000000100008338 // 得到元类XJTeacher的isa
0x1000082f8: 0x00000001006433a0 0x0003e03500000007
(lldb) p/x 0x00007fff932d80f0 & 0x00007ffffffffff8 // isa & ISA_MASK
(long) $5 = 0x00007fff932d80f0
(lldb) po 0x00007fff932d80f0
NSObject // 输出为NSObject,那么这是类还是元类呢
// NSObject lldb 调试
// 下面验证一下
(lldb) p/x [NSObject class] // 16进制输出NSObject的类的内存信息
(Class) $7 = 0x00007fff932d8118 NSObject // 得到根类NSObject的地址
(lldb) x/4gx 0x00007fff932d8118 // 16进制格式化输出根类NSObject的内存信息
0x7fff932d8118: 0x00007fff932d80f0 0x0000000000000000 // 得到根类NSObject的isa
0x7fff932d8128: 0x00000001004992d0 0x0001801000000003
(lldb) p/x 0x00007fff932d80f0 & 0x00007ffffffffff8 // isa & ISA_MASK
(long) $8 = 0x00007fff932d80f0 // 得到新的地址
(lldb) po 0x00007fff932d80f0 // po 输出
NSObject // 输出为NSObject,根类的元类,即根元类(RootMetaClass)
// 继续验证
(lldb) x/4gx 0x00007fff932d80f0
0x7fff932d80f0: 0x00007fff932d80f0 0x00007fff932d8118
0x7fff932d8100: 0x0000000100643750 0x0005e03100000007
(lldb) p/x 0x00007fff932d80f0 & 0x00007ffffffffff8
(long) $18 = 0x00007fff932d80f0
(lldb) po 0x00007fff932d80f0
NSObject
图解:
结论:
- 非根类
NSObject
类的实例对象的isa
指向类(Class
),类的isa
指向元类(MetaClass
),元类的isa
指向根元类(RootMetaClass
),而不是父类的元类。 - 根类
NSObject
类的实例对象的isa
指向根类(RootClass
),根类的isa
指向根元类(RootMetaClass
)。 - 根元类的
isa
还是指向根元类。
1.3 元类superclass
指向分析
代码实例:
#pragma mark - 元类的superclass链
void xjSuperclass(void) {
//NSObjcet实例对象
NSObject *objectInstance = [NSObject alloc];
//NSObject类
Class object = object_getClass(objectInstance);
//NSobject元类
Class metaClass = object_getClass(object);
//NSObjct根元类
Class rootMetaClass = object_getClass(metaClass);
NSLog(@"\n%p NSObject实例对象\n%p NSObject类\n%p NSObject元类\n%p NSObject根元类\n",objectInstance,object,metaClass,rootMetaClass);
// XJPerson的元类
Class personMetaClass = object_getClass(XJPerson.class);
NSLog(@"XJPerson的元类 : %@ - %p",personMetaClass, personMetaClass);
// XJPerson元类的父类
Class personSuperMetaclass = class_getSuperclass(personMetaClass);
NSLog(@"XJPerson元类的父类 : %@ - %p",personSuperMetaclass, personSuperMetaclass);
// XJTeacher的元类
Class teacherMetaClass = object_getClass(XJTeacher.class);
NSLog(@"XJTeacher的元类 : %@ - %p",teacherMetaClass,teacherMetaClass);
// XJTeacher元类的父类
Class childrenSuperMetaClass = class_getSuperclass(teacherMetaClass);
NSLog(@"XJTeacher元类的父类 : %@ - %p",childrenSuperMetaClass,childrenSuperMetaClass);
// NSObject类的父类
Class superObject = class_getSuperclass(NSObject.class);
NSLog(@"NSObject的父类 : %@ - %p",superObject,superObject);
//NSObject根元类的父类
Class rootSuperMetaObject = class_getSuperclass(rootMetaClass);
NSLog(@"NSObject根元类的父类 : %@ - %p",rootSuperMetaObject,rootSuperMetaObject);
}
********************** 打印输出结果 **********************
2021-06-19 23:10:35.443934+0800 002-isa分析[12886:361372]
0x100644850 NSObject实例对象
0x7fff932d8118 NSObject类
0x7fff932d80f0 NSObject元类
0x7fff932d80f0 NSObject根元类
2021-06-19 23:10:35.444578+0800 002-isa分析[12886:361372] XJPerson的元类 : XJPerson - 0x100008338
2021-06-19 23:10:35.444650+0800 002-isa分析[12886:361372] XJPerson元类的父类 : NSObject - 0x7fff932d80f0
2021-06-19 23:10:35.444800+0800 002-isa分析[12886:361372] XJTeacher的元类 : XJTeacher - 0x1000082e8
2021-06-19 23:10:35.444838+0800 002-isa分析[12886:361372] XJTeacher元类的父类 : XJPerson - 0x100008338
2021-06-19 23:10:35.444908+0800 002-isa分析[12886:361372] NSObject的父类 : (null) - 0x0
2021-06-19 23:10:35.444942+0800 002-isa分析[12886:361372] NSObject根元类的父类 : NSObject - 0x7fff932d8118
结论:
- 元类之间也存在继承链,与类一样。
- 根元类的父类是根类。
- 根类的父类是nil。
图解:
2:类的结构分析
本小节将对类的结构进行分析,前面探索过类在底层实现就是objc_class
结构体,源码里搜索struct objc_class
,会发现在objc4-818.2
源码里有三处定义:
2.1 ObjC 1.0的objc_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;
2.2 ObjC 2.0的objc_class
(基于objc4-818.2
)
2.2.1 objc-runtime-old.h
文件里objc_class
的定义
struct objc_class : objc_object {
Class superclass;
const char *name;
uint32_t version;
uint32_t info;
uint32_t instance_size;
struct old_ivar_list *ivars;
struct old_method_list **methodLists;
Cache cache;
struct old_protocol_list *protocols;
// CLS_EXT only
const uint8_t *ivar_layout;
struct old_class_ext *ext;
// 篇幅原因,省略大部分代码
}
2.2.2 objc-runtime-new.h
文件里objc_class
的定义
// 现行版本所使用的,我们的探索也是基于这个定义
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; // 8字节,隐藏变量isa,继承自objc_object
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-runtime-new.h
文件里objc_class
的定义可以看出一共有4个成员变量,分别为isa
(继承自objc_object
的隐藏变量)、superclass
、cache
和bits
,其中isa
和superclass
已经分析过了,这里不在赘述,查看cache
和bits
的注释和数据类型定义,终于在struct class_rw_t
里发现了方法、属性和协议的相关信息,接下来我们就对bits
进行相关探索、验证。
2.3 objc_class
成员变量bits
的分析
案例源码:
@interface XJPerson : NSObject
{
NSString *_age;
CGFloat _height;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *hobby;
- (void)sayNB;
+ (void)say666;
+ (void)heiha;
@end
已知类名,想要拿到objc_class
的成员变量bits
,需要通过内存平移的方式,下面就先简单介绍下内存平移。
2.3.1 内存平移
示例:
图解:
结论:知道一个数据结构的首地址后,可以通过内存平移的方式得到其中元素的信息。
2.3.2 类的结构内存计算
想要拿到bits
里的数据就得先从首地址平移到bits
相应的位置,已知isa
为8
字节,superclass
为8
字节,现在只需算出cache
的内存大小就可以了,cache
的数据结构为cache_t
的结构体,查看其定义(方法存在方法区,static
修饰的变量存在全局区,不占用结构体内存空间),发现cache_t
结构体占用内存大小为16
字节。
// uintptr_t的重命名
typedef unsigned long uintptr_t; // 8字节
// mask_t的重命名,当前为64位环境,所以为4字节
#if __LP64__
typedef uint32_t mask_t; // x86_64 & arm64 asm are less efficient with 16-bits
#else
typedef uint16_t mask_t;
#endif
struct cache_t { // 经过计算得出cache_t结构体占用内存为16字节
private:
explicit_atomic<uintptr_t> _bucketsAndMaybeMask; // 8字节
union { // 联合体,里面的结构体和_originalPreoptCache互斥,实际上他们都是8字节
struct {
explicit_atomic<mask_t> _maybeMask; // uint32_t 4字节
#if __LP64__
uint16_t _flags; // 2字节
#endif
uint16_t _occupied; // 2字节
}; // 结构体一共8字节
explicit_atomic<preopt_cache_t *> _originalPreoptCache; // 8字节
};
// 篇幅原因,省略大部分代码
// 下面都是static修饰的变量和方法,不占用结构体内存空间
}
图解:
2.3.3 获取类的属性
属性获取流程:XJPerson.class
-> class_data_bits_t
-> class_rw_t
-> property_array_t
-> property_list_t
-> property_t
。
lldb
调试流程:
(lldb) p/x XJPerson.class
// 拿到XJPerson类对象的地址
(Class) $0 = 0x0000000100008380 XJPerson
(lldb) x/4gx 0x0000000100008380
// 拿到XJPerson类对象的首地址
0x100008380: 0x00000001000083a8 0x0000000100357140
0x100008390: 0x000000010104e040 0x0002802800000003
// 首地址平移32位
(lldb) p/x 0x100008380 + 0x20
// 拿到class_data_bits_t bits的地址
(long) $1 = 0x00000001000083a0
// 类型装换
(lldb) p (class_data_bits_t *)0x00000001000083a0
(class_data_bits_t *) $2 = 0x00000001000083a0
// 指针访问成员变量、方法用`->`,结构体用`.`。
// 使用class_data_bits_t结构体里面的data()函数取出class_rw_t *
(lldb) p $2->data()
(class_rw_t *) $3 = 0x000000010104e000
// 使用class_rw_t结构体里面的properties()函数取出property_array_t
(lldb) p $3->properties()
(const property_array_t) $4 = {
list_array_tt<property_t, property_list_t, RawPtr> = {
= {
list = {
ptr = 0x0000000100008260
}
arrayAndFlag = 4295000672
}
}
}
// 取出list
(lldb) p $4.list
(const RawPtr<property_list_t>) $5 = {
ptr = 0x0000000100008260
}
// 取出ptr,得到property_list_t指针
(lldb) p $5.ptr
(property_list_t *const) $6 = 0x0000000100008260
// *号取值
(lldb) p *$6
// 得到property_list_t结构体实例,count为2
(property_list_t) $7 = {
entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2)
}
// 读取属性值
(lldb) p $7.get(0)
(property_t) $8 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
(lldb) p $7.get(1)
(property_t) $9 = (name = "hobby", attributes = "T@\"NSString\",C,N,V_hobby")
(lldb)
图解:
2.3.4 获取类的实例方法
实例方法与属性一样,都需要获取到class_rw_t
,然后调用methods()
函数获取到到方法列表,最后获取到method_t
,但是method_t
结构体里面没有成员变量,所以直接输出会显示为空,需要获取method_t
里面的big
结构体。
实例方法获取流程:XJPerson.class
-> class_data_bits_t
-> class_rw_t
-> method_array_t
-> method_list_t
-> method_t
->big
(big
不行就用small
)。
method_t
部分源码:
// 源码来着objc4-818.2->objc-runtime-new.h->726-861
struct method_t {
static const uint32_t smallMethodListFlag = 0x80000000;
method_t(const method_t &other) = delete;
// The representation of a "big" method. This is the traditional
// representation of three pointers storing the selector, types
// and implementation.
struct big {
SEL name;
const char *types;
MethodListIMP imp;
};
// 篇幅原因,省略大部分代码
}
lldb
调试流程:
(lldb) p/x XJPerson.class
// 拿到XJPerson类对象的地址
(Class) $0 = 0x0000000100008380 XJPerson
(lldb) x/4gx 0x0000000100008380
// 拿到XJPerson类对象的首地址
0x100008380: 0x00000001000083a8 0x0000000100357140
0x100008390: 0x0000000100682630 0x0002802800000003
// 首地址平移32位
(lldb) p/x 0x100008380 + 0x20
// 拿到class_data_bits_t bits的地址
(long) $1 = 0x00000001000083a0
// 类型装换
(lldb) p/x (class_data_bits_t *)0x00000001000083a0
(class_data_bits_t *) $2 = 0x00000001000083a0
// 使用class_data_bits_t结构体里面的data()函数取出class_rw_t *
(lldb) p $2->data()
(class_rw_t *) $3 = 0x00000001006825f0
// 使用class_rw_t结构体里面的methods()函数取出method_array_t
(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
}
}
}
// 取出list
(lldb) p $4.list
(const method_list_t_authed_ptr<method_list_t>) $5 = {
ptr = 0x0000000100008160
}
// 取出ptr,得到method_list_t指针
(lldb) p $5.ptr
(method_list_t *const) $6 = 0x0000000100008160
// *号取值
(lldb) p *$6
// 得到method_list_t结构体实例,count为6
(method_list_t) $7 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 6)
}
// 通过get()函数拿到method_t,然后通过big()函数读出实例方法信息
(lldb) p $7.get(0).big()
(method_t::big) $8 = {
name = "sayNB"
types = 0x0000000100003f7a "v16@0:8"
imp = 0x0000000100003d40 (KCObjcBuild`-[XJPerson sayNB])
}
(lldb) p $7.get(1).big()
(method_t::big) $9 = {
name = "hobby"
types = 0x0000000100003f72 "@16@0:8"
imp = 0x0000000100003db0 (KCObjcBuild`-[XJPerson hobby])
}
(lldb) p $7.get(2).big()
(method_t::big) $10 = {
name = "setHobby:"
types = 0x0000000100003f8e "v24@0:8@16"
imp = 0x0000000100003de0 (KCObjcBuild`-[XJPerson setHobby:])
}
(lldb) p $7.get(3).big()
(method_t::big) $11 = {
name = "init"
types = 0x0000000100003f72 "@16@0:8"
imp = 0x0000000100003ce0 (KCObjcBuild`-[XJPerson init])
}
(lldb) p $7.get(4).big()
(method_t::big) $12 = {
name = "name"
types = 0x0000000100003f72 "@16@0:8"
imp = 0x0000000100003d50 (KCObjcBuild`-[XJPerson name])
}
(lldb) p $7.get(5).big()
(method_t::big) $13 = {
name = "setName:"
types = 0x0000000100003f8e "v24@0:8@16"
imp = 0x0000000100003d80 (KCObjcBuild`-[XJPerson setName:])
}
(lldb)
图解:
2.3.5 获取类的成员变量(ivars
)
成员变量(ivars
)获取流程:XJPerson.class
-> class_data_bits_t
-> class_rw_t
-> class_ro_t
-> ivar_list_t
-> ivar_t
。
成员变量(ivars
)与属性不同,存在class_ro_t
结构体里。
class_ro_t
部分源码:
// 源码来自objc4-818.2->objc-runtime-new.h->1037-1171
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
union {
const uint8_t * ivarLayout;
Class nonMetaclass;
};
explicit_atomic<const char *> name;
// With ptrauth, this is signed if it points to a small list, but
// may be unsigned if it points to a big list.
void *baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars; // 成员变量
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
// 篇幅原因,省略大部分代码
}
// 源码来自objc4-818.2->objc-runtime-new.h->863-883
struct ivar_t {
#if __x86_64__
// *offset was originally 64-bit on some x86_64 platforms.
// We read and write only 32 bits of it.
// Some metadata provides all 64 bits. This is harmless for unsigned
// little-endian values.
// Some code uses all 64 bits. class_addIvar() over-allocates the
// offset for their benefit.
#endif
int32_t *offset;
const char *name;
const char *type;
// alignment is sometimes -1; use alignment() instead
uint32_t alignment_raw;
uint32_t size;
uint32_t alignment() const {
if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT;
return 1 << alignment_raw;
}
};
lldb
调试流程:
// 省略获取class_rw_t的流程
(class_rw_t *) $3 = 0x00000001007cfe30
// 调用class_rw_t的ro()函数获取class_ro_t
(lldb) p $3->ro()
(const class_ro_t *) $4 = 0x0000000100008110
// 取值
(lldb) p *$4
// 得到class_ro_t结构体
(const class_ro_t) $5 = {
flags = 0
instanceStart = 8
instanceSize = 40
reserved = 0
= {
ivarLayout = 0x0000000000000000
nonMetaclass = nil
}
name = {
std::__1::atomic<const char *> = "XJPerson" {
Value = 0x0000000100003e96 "XJPerson"
}
}
baseMethodList = 0x0000000100003de0
baseProtocols = nil
ivars = 0x0000000100008158 // 成员变量
weakIvarLayout = 0x0000000000000000
baseProperties = 0x00000001000081e0
_swiftMetadataInitializer_NEVER_USE = {}
}
// `.`语法取出成员变量列表
(lldb) p $5.ivars
// 获得ivar_list_t指针
(const ivar_list_t *const) $6 = 0x0000000100008158
// 取值
(lldb) p *$6
// 获得ivar_list_t,count为4
(const ivar_list_t) $7 = {
entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 4)
}
// 通过get()函数拿到ivar_t
(lldb) p $7.get(0)
(ivar_t) $8 = {
offset = 0x00000001000082bc
name = 0x0000000100003f10 "_age"
type = 0x0000000100003f55 "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $7.get(1)
(ivar_t) $9 = {
offset = 0x00000001000082c0
name = 0x0000000100003f15 "_height"
type = 0x0000000100003f7c "d"
alignment_raw = 3
size = 8
}
(lldb) p $7.get(2)
(ivar_t) $10 = {
offset = 0x00000001000082c4
name = 0x0000000100003f1d "_name"
type = 0x0000000100003f55 "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $7.get(3)
(ivar_t) $11 = {
offset = 0x00000001000082c8
name = 0x0000000100003f23 "_hobby"
type = 0x0000000100003f55 "@\"NSString\""
alignment_raw = 3
size = 8
}
图解:
2.3.6 获取类的类方法
类方法存在元类中,所以需要先获取元类,然后获取元类的方法列表,才能获取到类方法。
类方法获取流程:XJPerson.class
-> Metaclass
-> class_data_bits_t
-> class_rw_t
-> method_array_t
-> method_list_t
-> method_t
->big
(big
不行就用small
)。
lldb
调试流程:
// M1 iMac调试
(lldb) p/x XJPerson.class
(Class) $0 = 0x0000000100008328 XJPerson
(lldb) x/4gx 0x0000000100008328
0x100008328: 0x0000000100008350 0x0000000100379140
0x100008338: 0x000000010036d0f0 0x0000803000000000
(lldb) p/x 0x0000000100008350 & 0x0000000ffffffff8
(long) $1 = 0x0000000100008350
(lldb) x/4gx 0x0000000100008350
0x100008350: 0x00000001003790f0 0x00000001003790f0
0x100008360: 0x00010001009065f0 0x0002e03400000000
(lldb) p/x 0x100008350 + 0x20
(long) $2 = 0x0000000100008370
(lldb) p/x (class_data_bits_t *)0x0000000100008370
(class_data_bits_t *) $3 = 0x0000000100008370
(lldb) p $3->data()
(class_rw_t *) $4 = 0x0000000100a10e10
(lldb) p $4->methods()
(const method_array_t) $5 = {
list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
= {
list = {
ptr = 0x0000000100003db8
}
arrayAndFlag = 4294983096
}
}
}
(lldb) p $5.list
(const method_list_t_authed_ptr<method_list_t>) $6 = {
ptr = 0x0000000100003db8
}
(lldb) p $6.ptr
(method_list_t *const) $7 = 0x0000000100003db8
(lldb) p *$7
(method_list_t) $8 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 2147483660, count = 2)
}
(lldb) p $8.get(0).small()
(method_t::small) $9 = {
name = (offset = 17608)
types = (offset = 419)
imp = (offset = -272)
}
(lldb) p $8.get(1).small()
(method_t::small) $10 = {
name = (offset = 17604)
types = (offset = 407)
imp = (offset = -264)
}
(lldb) p $8.get(0).getDescription()
(objc_method_description *) $9 = 0x0000000100717a80
(lldb) p *$9
(objc_method_description) $10 = (name = "say666", types = "v16@0:8")
(lldb) p *($8.get(1).getDescription())
(objc_method_description) $11 = (name = "heiha", types = "v16@0:8")
图解:
结论:类是元类的类对象,且具有唯一性,类方法即是元类的实例方法。
2.3.7 获取类的协议
想要获取类的协议(protocol
),必要条件是需要先遵守协议。
协议获取流程:XJPerson.class
-> class_data_bits_t
-> class_rw_t
-> protocol_array_t
-> protocol_list_t
-> protocol_ref_t
-> protocol_t
-> method_list_t
-> method_t
-> big
(big
不行就用small
)。
protocol_list_t
、protocol_ref_t
、protocol_t
部分源码:
测试代码示例:
lldb
调试流程:
(lldb) p/x XJPerson.class
(Class) $0 = 0x0000000100008778 XJPerson
(lldb) p/x 0x0000000100008778 + 0x20
(long) $1 = 0x0000000100008798
(lldb) p (class_data_bits_t *)0x0000000100008798
(class_data_bits_t *) $2 = 0x0000000100008798
(lldb) p $2->data()
(class_rw_t *) $3 = 0x00000001009c5d90
(lldb) p $3->protocols()
(const protocol_array_t) $4 = {
list_array_tt<unsigned long, protocol_list_t, RawPtr> = {
= {
list = {
ptr = 0x0000000100008168
}
arrayAndFlag = 4295000424
}
}
}
(lldb) p $4.list
(const RawPtr<protocol_list_t>) $5 = {
ptr = 0x0000000100008168
}
(lldb) p $5.ptr
(protocol_list_t *const) $6 = 0x0000000100008168
(lldb) p *$6
(protocol_list_t) $7 = (count = 2, list = protocol_ref_t [] @ 0x00000001196abc58)
(lldb) p $7.list[0]
(protocol_ref_t) $8 = 4295002064
(lldb) p (protocol_t *)4295002064
(protocol_t *) $9 = 0x00000001000087d0
p $9[0]
(protocol_t) $10 = {
objc_object = {
isa = {
bits = 4298608840
cls = Protocol
= {
nonpointer = 0
has_assoc = 0
has_cxx_dtor = 0
shiftcls = 537326105
magic = 0
weakly_referenced = 0
unused = 0
has_sidetable_rc = 0
extra_rc = 0
}
}
}
mangledName = 0x0000000100003e52 "XJPersonDelegate"
protocols = 0x00000001000085b0
instanceMethods = 0x00000001000085c8
classMethods = nil
optionalInstanceMethods = nil
optionalClassMethods = nil
instanceProperties = nil
size = 96
flags = 0
_extendedMethodTypes = 0x0000000100008600
_demangledName = 0x0000000000000000
_classProperties = nil
}
(lldb) p $10.instanceMethods
(method_list_t *) $11 = 0x00000001000085c8
(lldb) p $11->get(0).big()
(method_t::big) $12 = {
name = "delegateMethod1"
types = 0x0000000100003e77 "v16@0:8"
imp = 0x0000000000000000
}
(lldb) p $11->get(1).big()
(method_t::big) $13 = {
name = "delegateMethod2:"
types = 0x0000000100003ea9 "@24@0:8@16"
imp = 0x0000000000000000
}
(lldb) p $9[1]
(protocol_t) $14 = {
objc_object = {
isa = {
bits = 4298608840
cls = Protocol
= {
nonpointer = 0
has_assoc = 0
has_cxx_dtor = 0
shiftcls = 537326105
magic = 0
weakly_referenced = 0
unused = 0
has_sidetable_rc = 0
extra_rc = 0
}
}
}
mangledName = 0x0000000100003e36 "XJPersonDataSource"
protocols = 0x0000000100008278
instanceMethods = 0x0000000100008290
classMethods = nil
optionalInstanceMethods = nil
optionalClassMethods = nil
instanceProperties = nil
size = 96
flags = 0
_extendedMethodTypes = 0x00000001000082c8
_demangledName = 0x0000000000000000
_classProperties = nil
}
(lldb) p $14.instanceMethods
(method_list_t *) $15 = 0x0000000100008290
(lldb) p $15->get(0).big()
(method_t::big) $16 = {
name = "dataSourceMethod1"
types = 0x0000000100003e77 "v16@0:8"
imp = 0x0000000000000000
}
(lldb) p $15->get(1).big()
(method_t::big) $17 = {
name = "dataSourceMethod2:"
types = 0x0000000100003ea9 "@24@0:8@16"
imp = 0x0000000000000000
}
图解: