类分析初探
基于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个

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
不是元类。

查看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();
}

查看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
内的信息。

查看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
内容,但是仅找到了实例方法sayHello
,未找到类方法testClassMethod
。

网友评论