网络中有个关于类的关系的图
图中虚线部分是isa的的继承关系
证明如下图
有关lldb指令的说明:
- p/x 以16进制形式打印
- x/4gx 读取内存地址,冒号左边为地址,冒号右边为地址存储的值
- po 打印实现了description方法
- 对象->类
//打印16进制person的内存地址
(lldb) p/x person
(LGPerson *) $26 = 0x00000001006e8400
//读取person的内存地址
(lldb) x/4gx 0x00000001006e8400
0x1006e8400: 0x001d8001000020f1 0x0000000000000000
0x1006e8410: 0x00000001006e84e0 0x00000001006e8720
//isa指针与上ISA_MASK 0x00007ffffffffff8ULL得到类信息
(lldb) p/x 0x001d8001000020f1 & 0x00007ffffffffff8ULL
//得到类的信息
(unsigned long long) $27 = 0x00000001000020f0
(lldb) po 0x00000001000020f0
LGPerson
其中0x001d8001000020f1
是类的isa指针,
- 类->元类
//读取类的地址的内存
(lldb) x/4gx 0x00000001000020f0
0x1000020f0: 0x00000001000020c8 0x0000000100334140
0x100002100: 0x000000010032e410 0x0000801000000000
(lldb) p/x 0x00000001000020c8 & 0x00007ffffffffff8ULL
//元类地址
(unsigned long long) $29 = 0x00000001000020c8
(lldb) po 0x00000001000020c8
LGPerson
其中0x00000001000020c8
是元类的isa指针
- 元类->根元类
//读取元类的内存地址
(lldb) x/4gx 0x00000001000020c8
0x1000020c8: 0x00000001003340f0 0x00000001003340f0
0x1000020d8: 0x00000001006420f0 0x0004e03100000007
(lldb) po 0x00000001003340f0
NSObject
其中0x00000001003340f0
是根元类的isa指针
- 根元类->根元类
(lldb) p/x 0x00000001003340f0 & 0x00007ffffffffff8ULL
(unsigned long long) $32 = 0x00000001003340f0
(lldb) x/4gx 0x00000001003340f0
0x1003340f0: 0x00000001003340f0 0x0000000100334140
0x100334100: 0x00000001006e6a50 0x0004e03100000007
(lldb) po 0x00000001003340f0
NSObject
类的继承
在objc-private.h中的objc_object是根对象
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
省略号
};
objc_class也是继承自objc_object
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
省略号
}
objc的源码地址
所以objc_object是万物的根源。所有的类都是由它的模板创建而来。上层通过底层objc的提供的方法创建对象。
类的结构
源码在objc-runtime-new.h中
struct objc_class : objc_object {
// Class ISA; isa指针8个字节
Class superclass; //父类8个字节
cache_t cache; // formerly cache pointer and vtable 16
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() const {
return bits.data();
}
//省略号
//下面的都是方法,方法不占用结构体内存
}
其中isa8个字节,父类superclass8个字节,cache占16个,根据c语言规范,要读取bits内容需要从objc_class的首地址开始偏移32位读取。
创建类
@interface LGPerson : NSObject
@property(nonatomic,copy) NSString *nickName;
@property(nonatomic,copy) NSString *identifer;
-(void)sayHello;
+(void)sayWorld;
@end
#import "LGPerson.h"
@implementation LGPerson
-(void)sayHello{
}
+(void)sayWorld{
}
@end
如图
- 获取类的首地址
0x0000000100002248
,objc_class是一个结构体,其中isa8个字节,父类superclass8个字节,cache占16个,要读取bits需要从类的首地址开始偏移32位读取。cache通过查看其源码得知是16位。 - 此时
$1
为bits,调用其data()方法,返回一个class_rw_t类型的数据$3
,返回的数据信息不熟悉。查看class_rw_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};
}
}
- 控制台打印class_rw_t的properties()方法
(const property_array_t) $4 = {
list_array_tt<property_t, property_list_t> = {
= {
list = 0x00000001000021d0
arrayAndFlag = 4294975952
}
}
}
(lldb) p $4.list
(property_list_t *const) $5 = 0x00000001000021d0
(lldb) p *$5
(property_list_t) $6 = {
entsize_list_tt<property_t, property_list_t, 0> = {
entsizeAndFlags = 16
count = 2
first = (name = "nickName", attributes = "T@\"NSString\",C,N,V_nickName")
}
}
这个properties()方法可以获取我们在LGPerson中定义的属性如下图
同理,我们可以获取类中的methods
- 调用class_rw_t (
$3
)的methods()方法,返回方法信息 - 读取list中的信息,发现有6个方法,
- 在LGPerson中定义的方法sayHello
- 属性nikName和identifier的get和set方法
- .cxx_destruct方法
- 发现我们定义的类方法并不存在这个metho_list中。
类方法
类方法不在类中,那么根据第一张图,类方法是否在元类中
lldb断点查看LGPerson可以发现类方法存在与元类中
class的结构初步分析如上