在OC底层探索06-isa本身藏了多少信息你知道吗?分析了isa
。
在平时的开发中应该都接触或者使用过
缓存
的技术,目的就是提高执行效率,用空间换取时间
。当然apple在这方面一定也有其特别的地方。
// 再熟悉一下objc_class的结构
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache;
class_data_bits_t bits;
...
}
objc_object
中的cache
提到缓存那么cache
面缓存的是什么呢:属性还方法?其实很好猜测,平时开发中使用最多的就是方法,因为只有方法才会引起变化
。下面会通过两种方式进行验证这个猜测。
struct cache_t {
//不同设备环境数据结构不同
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
explicit_atomic<struct bucket_t *> _buckets;
explicit_atomic<mask_t> _mask;
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
explicit_atomic<uintptr_t> _maskAndBuckets;
mask_t _mask_unused;
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
explicit_atomic<uintptr_t> _maskAndBuckets;
mask_t _mask_unused;
uint16_t _flags;
uint16_t _occupied;
...
}
struct bucket_t {
#if __arm64__
explicit_atomic<uintptr_t> _imp; //方法指针
explicit_atomic<SEL> _sel; //方法标示
#else
explicit_atomic<SEL> _sel;
explicit_atomic<uintptr_t> _imp;
#endif
}
explicit_atomic<X>
:类似于swift
中的泛型。将当前类型设置为原子性
,也就是将其设置为线程安全。(毕竟在多线程中调用方法的场景太多了)_buckets | _maskAndBuckets
中objc-781
的 objc_class结构struct objc_object { //所有对象的模板类
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
struct objc_class : objc_object {
// Class ISA; //偏移量:8位
Class superclass; //偏移量:8位
cache_t cache; //偏移量:8 + 4 + 2 + 2 = 16位
class_data_bits_t bits;
class_rw_t *data() const {
return bits.data();
}
...
}
这lldb中我们无法直接访问objc_class
中的信息,只能通过指针访问的方式来进行验证,所以这里需要用到内存偏移
int c[4] = {1, 2, 3, 4};
int *d = c;
NSLog(@"%p -- %p - -- %p --- %p", &c, &c[0], &c[1],&c[2]);
NSLog(@"%p -- %p - %p", d, d+1, d+2);
企业微信截图_7568a4b2-695a-48d0-97ee-303210e61466.png
地址进行+4来访问数组的下一个成员。
手动计算objc_class
源码中class_data_bits_t bits;
的偏移量:32位
,(在加上结构体:class_data_bits_t的8位,正好40位。印证了上文中的猜测)
(lldb) p/x HRTest.class
(Class) $0 = 0x00000001000033c0 HRTest //类头地址
(lldb) p (class_data_bits_t *)0x00000001000033d0 //类地址偏移0x20
(class_data_bits_t *) $1 = 0x00000001000033d0
(lldb) p $1->data()
(class_rw_t *) $2 = 0x00000001000033e8 //数据都存放在class_rw_t里
注:此处只放出和目标有关的信息
const class_ro_t *ro()
const method_array_t methods() //对象方法列表
const property_array_t properties() //属性
const protocol_array_t protocols() //类的协议
(class_rw_t *) $2 = 0x00000001012b46c0 //class_rw_t
(lldb) p $2->methods() //方法list
(const method_array_t) $3 = {
list_array_tt<method_t, method_list_t> = {
= {
list = 0x0000000100002108
arrayAndFlag = 4294975752
}
}
}
(lldb) p $3.list
(method_list_t *const) $4 = 0x0000000100002108
(lldb) p *$4
(method_list_t) $5 = {
...
}
(lldb) p $5.get(0)
(method_t) $6 = {
name = "say222" //对象方法say222
types = 0x0000000100000f73 "v16@0:8"
imp = 0x0000000100000d50 (HRTest`-[HRTest say222])
}
(lldb) p $5.get(1)
(method_t) $7 = {
name = ".cxx_destruct"
types = 0x0000000100000f73 "v16@0:8"
imp = 0x0000000100000d60 (HRTest`-[HRTest .cxx_destruct])
}
(lldb) p $5.get(2)
(method_t) $8 = {
name = "name" //自动生成属性的get方法
types = 0x0000000100000f87 "@16@0:8"
imp = 0x0000000100000da0 (HRTest`-[HRTest name])
}
(lldb) p $5.get(3)
(method_t) $9 = {
name = "setName:" //自动生成属性的set方法
types = 0x0000000100000f8f "v24@0:8@16"
imp = 0x0000000100000dd0 (HRTest`-[HRTest setName:])
}
(lldb) p $5.get(4)
Assertion failed: (i < count)... //数组越界了
找到了对象方法
,属性的get,set
方法,唯独没有知道类方法。
(lldb) x/gx HRTest.class
0x100002218: 0x00000001000021f0 //类的元类
(lldb) p (class_data_bits_t *)0x0000000100002210
(class_data_bits_t *) $1 = 0x0000000100002210
(lldb) p $1->data()
(class_rw_t *) $2 = 0x00000001015338d0
(lldb) p $2->methods()
(const method_array_t) $3 = {
list_array_tt<method_t, method_list_t> = {
= {
list = 0x00000001000020a0
arrayAndFlag = 4294975648
}
}
}
(lldb) p $3.list
(method_list_t *const) $4 = 0x00000001000020a0
(lldb) p *$4
(method_list_t) $5 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 26
count = 1
first = {
name = "say666"
types = 0x0000000100000f73 "v16@0:8"
imp = 0x0000000100000d40 (HRTest`+[HRTest say666])
}
}
}
类方法say666
查看方式与方法相同:properties()
(class_rw_t *) $2 = 0x0000000101822660 //class_rw_t
(lldb) p $2->ro() //ro
(const class_ro_t *) $3 = 0x00000001000020c0
(lldb) p $3->ivars //成员变量
(const ivar_list_t *const) $4 = 0x0000000100002170
(lldb) p *$4 //取地址
(const ivar_list_t) $5 = {
...
}
(lldb) p $5.get(0)
(ivar_t) $7 = { //定义的成员变量
offset = 0x00000001000021e0
name = 0x0000000100000ed2 "HRTestName"
type = 0x0000000100000f7b "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $5.get(1)
(ivar_t) $8 = { //name属性自定生成的:_name成员变量
offset = 0x00000001000021e8
name = 0x0000000100000edd "_name"
type = 0x0000000100000f7b "@\"NSString\""
alignment_raw = 3
size = 8
}
class_rw_t
->ro()
->ivars
中_属性名
class_rw_ext_t
这个结构中。超过90%的普通类都不会动态进行修改方法、协议、属性
,所以大多数只需要class_rw_t
这个结构就完整了。在objc4-781中method_t
结构是这样
struct method_t {
SEL name;
const char *types;
MethodListIMP imp;
在objc4-818.2版本中method_t
结构在发生了变化,增加了一个结构体嵌套:
struct method_t {
...
struct big {
SEL name;
const char *types;
MethodListIMP imp;
};
所以在lldb调试的时候,会有一点变化