1.isa指针指向解析
1.1类对象的获取
通过以下三种方式获取类对象,输出的类对象地址相同,说明类对象有且只有一个。
1.2 类对象的本质:
从源码中可以看到Class就是objc_class结构体指针,可以看到可对象中也有ISA指针,实例对象的isa指针指向类对象,类对象的isa指针指向哪里?;
1.3 类对象isa指针的探索
对实例对象的isa指针&ISA_MASK可以得到类对象,我们同样对类对象的isa&ISA_MASK可以得到一个指针,po打印指针,得到也是对象名称,我们从上面得到类对象是唯一的,那么这个指针不是类对象,这个对象是元类对象,可以得到:
- 实例对象isa-->类对象,类对象的isa-->元类对象,元类对象isa->根元类,根元类isa--> 根元类;
- NSObject isa --> 根元类,根元类isa-->根元类;
- 元类和类对象名称相同;
2.元类的继承关系
有两个类LGPerson和LGTeacher,LGTeacher继承LGPerson,LGPerson继承NSObject:
- 根元类继承NSObject;
- NSObject没有父类;
- 元类继承父类的元类;
3. 内存平移
举例,如下图:
c是数组的首指针,d也指向输出的首地址;d+1是内存平移,平移了4个字节,因为数组是int类型,可以用d+1指向数组的下一个地址,从而输出该值;输出如下:
4.实例方法,属性的存储位置解析:
4.1 class_data_bits_t
类对象中class_data_bits_t bits前面有isa占8个字节,Class superclass占8个字节、cache_t cache 占16个字节,可以根据类对象的首地址,根据内存平移32个字节得到bits的首地址:
4.2 实例方法存储位置解析步骤
4.2.1 获取class_data_bits_t
可以看出输出的bits,不能看得到有什么有用的内存,可以通过分析class_data_bits_t中源码找到:
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
4.2.2获取class_rw_t
class_rw_t 中存储了实例方法,属性,协议等,调用data方法得到class_rw_t *,然后输出内容,可以看出,只能从源码寻找class_rw_t存储的内从;
p $3.data()
(class_rw_t *) $4 = 0x00000001006055e0
从源码中可以看到class_rw_t存储了实例方发,属性协议如下
4.2.3调用methods()
可以得到method_array_t数组,取list值中prt如下:
4.2.4 method_list_t
上一步找到method_list_t,查看源码,继承entsize_list_tt,可以看到entsize_list_tt是一个容器,我们从源码中找到遍历的迭代器是get方法
p $10.get(0)**
(method_t) $11 = {}
4.2.5 method_t
得到了method_t;去查看method_t源码
在iOS中方法是imp,我们找到imp,如上图,可以用big()方法获取如下:
可以得到methodShow方法;
- name值得是方法名
- types 方法类型
- imp 函数实现
可以看到有7个方法,其中.cxx_destruct是在ARC的模式下释放成员变量的析构方法,当类中有成员变量的时候,就会自动生成,property⽣成的实例变量也算,且⽗类的实例变量不会导致⼦类拥有这个⽅法。
4.2.5 types 方法类型后面的参数解析
$18 = {
name = "setAge:"
types = 0x0000000100003f96 "v24@0:8q16"
imp = 0x0000000100003d60 (JQObjcBuildDemo`-[LGPerson setAge:])
}
-(void)setAge:(int)age 中 "v24@0:8q16"
1 .任何方法都默认有两个参数的,id类型的self,和SEL类型的_cmd
2. v是指返回值地址下表:
举例:
-(void)setAge:(NSInteger)age 中 "v24@0:8q16"
v 24 @ 0 :8 q16
void(返回类型) 参数总长度 id SEL NSInteger
//参数的总占用空间为 8 + 8 + 8 = 24
//id从0开始占8个字节
//SEL从8开始8个字节
//NSInteger从16开始占8个字节
4.2.6 大端和小端
计算机硬件有两种储存数据的方式:大端字节序(big endian)和小端字节序(little endian)。在获取方法名的时候我们调用了big()放发,但是在不同的CPU下,不能通过此方法获取到方法名,可以通过small()方法获取;
举例来说,数值0x3311使用两个字节储存:高位字节是0x33,低位字节是0x11。
- 大端字节序:高位字节在前,低位字节在后,这是人类读写数值的方法。
- 小端字节序:低位字节在前,高位字节在后,即以
0x1122形式储存。0x1234567的大端字节序和小端字节序的写法如下图:
4.3 属性的存储位置
找到 class_rw_t 中属性列表获取方法 properties()如图:
获取property_list_t如下:
property_list_tt继承于property_list_t其中
struct property_t {
const char *name;
const char *attributes;
};
5.总结
- 类对象有且只有一个,类对象的本质是objc_class结构体,类对象中存储了类的父类、属性、实例方法、协议、成员变量、类方法、方法缓存等;
- isa的指向关系:
- 实例对象的isa->类对象
- 类对象的isa->元类
- 元类的isa->根元类
- 根元类的isa->根元类自己
- 元类的继承关系:
-
父类的元类就是元类的父类
-
根元类的父类就是NSObject
-
NSObject没有父类
-
NSObject是万类之祖