类分析初探
基于isa结构分析 ,我们可以通过lldb获取对象的内存情况
创建一个Person类对象
Person *person = [[Person alloc] init];
查看类对象的内存信息
-
lldb命令 x/4gx person获取person内存,首地址代表当前对象也代表isa。NSObject的第一个成员变量为isa
person内存情况.png
-
将首地址与
ISA_MASK进行与运算,得到当前类信息x86_64 ISA_MASK 的值为 0x00007ffffffffff8ULL
当前类信息.png
-
继续查看当前类的内存分布
类的内存分布.png
元类
-
查看类的首地址,再次找到
Person。在Apple中,我们简称
Person类的类为元类。这个
0x0000000100002158下的Person就是元类。类的归属是元类,元类的定义和创建都由编译器自动完成
元类.png
-
查看元类的首地址,输出为
NSOjbect。
根元类.png
-
输出
NSOjbect类的首地址,NSObject的元类也是NSObject。NSOjbect也叫做根元类。
NSObject的首地址.png
查看类对象内存存在个数
代码
void testClassNum(){
Class class1 = [Person class];
Class class2 = [Person alloc].class;
Class class3 = object_getClass([Person alloc]);
NSLog(@"\nclass1 -%p\nclass2 -%p\nclass3 -%p\n",class1,class2,class3);
}
结果
类对象在内存中仅存在1个
类对象内存存在个数.png
isa 流程图
isa的指向 :
-
NSObject: 实例对象->根类->根元类 -
Person:实例对象->类->元类->根元类 -
Person的子类: 实例对象->类->元类->父元类->根元类
isa流程图.png
objc_object & objc_class
我们通过isa结构分析 中编译到main.cpp打开,可以找到几乎所有的类的结构体都是objc_object,所以objc_object是根对象。
objc_object
typedef struct objc_object NSObject;
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
objc_class
同样的,在main.cpp中我们可以找到,这个Class是objc_class重定义的,所以说类的底层是objc_class类型。
struct NSObject_IMPL {
Class isa;
};
typedef struct objc_class *Class;
在objc/objc-runtime-new.h中我们找到objc_class 是继承于objc_object的,所以对象、类和元类都有 isa。
struct objc_class : objc_object
类结构源码
在objc/objc-runtime-new.h中查看objc_class的源码,我们通过lldb辅助调试。
结构
-
isa,继承于objc_object -
superclass,父类 -
cache,缓存 bits
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
lldb调试
我们查看到第一段地址依旧是isa,第二段地址是superclass,通过p/x NSObject.class,可以知道第二段地址的NSObject不是元类。
类的内存查看.png
查看bits的内存
获取bits首地址
要想知道bits的首地址,已知isa为8字节,superclass为8字节,需要先算出cache的内存大小,从首地址进行偏移后获取到bits的首地址。
-
计算
cache_t的大小cache_t大小为16字节,首地址偏移8+8+16字节可得到bits首地址。
struct cache_t { #if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED explicit_atomic<struct bucket_t *> _buckets; // 是一个结构体指针类型,占8字节 explicit_atomic<mask_t> _mask; //是mask_t 类型,而 mask_t 是 unsigned int 的别名,占4字节 #elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16 explicit_atomic<uintptr_t> _maskAndBuckets; //是指针,占8字节 mask_t _mask_unused; //是mask_t 类型,而 mask_t 是 uint32_t 类型定义的别名,占4字节 #if __LP64__ uint16_t _flags; //是uint16_t类型,uint16_t是 unsigned short 的别名,占 2个字节 #endif uint16_t _occupied; //是uint16_t类型,uint16_t是 unsigned short 的别名,占 2个字节 -
获取偏移后的地址
获取bits地址.png
查看class_rw_t
根据class_data_bits_t开放的data()获取到class_rw_t
class_rw_t *data() const {
return bits.data();
}
获取class_rw_t.png
查看properties()
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};
}
}
Person新增一个属性name
@property (nonatomic, copy) NSString *name
通过lldb获取property_array_t内的信息。
property_list.png
查看methods()
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()};
}
}
Person新增一个实例方法sayHello,一个类方法testClassMethod;
- (void)say;
+ (void)testClassMethod;
通过lldb获取method_array_t内的信息。
先获取methods地址
methods.png
查看methods内容,但是仅找到了实例方法sayHello,未找到类方法testClassMethod。
method_array_content.png














网友评论