1、元类MetaClass
首先,先来看看类是什么,类在OC中其实是一个指向objc_class的结构体指针
,我们先看看结构体的构造,很多在OBJC2中即将废弃(单纯参考一下大概需要什么):
现有的为:
OC中对象objc_object
的定义是:
这里又涉及到一个知识点__has_feature(ptrauth_calls)
,我们下边再补充
OC对象有一个大家都熟悉的特性:消息发送机制
- 原理是OC对象在发送消息时,
运行时库
会根据对象的isa指针,得到对象所属的类,这个类包含了这个类的所有实例方法以及指向父类的指针,以便可以找到父类的实例方法。运行时库检查这个类和其父类的方法列表,找到与消息对应的方法。编译器会将消息转换为消息函数objc_msgSend
进行调用。 - OC的类其实也是一个对象,一个对象就要有一个它属于的类,意味着类也要有一个isa指针,指向他所属的类,所以,
元类就是类所属的类
。- 当你给对象发送消息时,消息是在寻找这个对象的类的方法列表。
- 当你给类发消息时,消息是在寻找这个类的元类的方法列表。
元类定义
-
元类就是类所属的类,提供类的通用方法列表
-
isa指向上
根元类是终点
:- 对象isa -> 类isa -> 元类isa -> 父元类isa -> 根元类isa -> 根元类
- 根类isa -> 根元类isa
-
继承关系上
NSObject是终点
:- 元类 -> 父元类 -> 根元类 -> 根类NSObject
__has_feature(ptrauth_calls)介绍
2、类结构分析
普通指针
- a 和 b 为变量都指向10, 10是
系统开辟的固定内存空间
, 其他需要10的值的变量都可以指向内存固定生成的10 - a 和 b 地址不一样, 这是一种拷贝, 属于
值拷贝
, 可发现a, b地址相差4个字节,stra、strb地址相差8个字节,这取决于a、b和stra和strb的类型
对象指针
- p1/p2 是
一级指针
, 指向的 alloc 开辟空间的内存地址 - &p1/&p2 是
二级指针
, 指向对象的指针的地址(0x7ffeefbff3f8, 0x7ffeefbff3f0 为对象指针)
数组指针
- &c == &c[0] == 首地址, 其实他都是取的首地址, 数组地址其实就是数组第一个元素地址即数组名为
首地址
- &c[0]与&c[1]相差
4
字节, 取决于数据类型
- 数组类型指针可以通过
首地址+偏移量
得到其他元素(偏移量为数组下标) - 移动的字节数 等于 偏移量 * 数据类型字节数, 这个根据&c[0], &c[1]看出, 两者相差4
3、类的结构内存
bits
中存放类信息
,可以通过偏移获取(32字节即0x20
)class_data_bits_t bits内容
- 计算偏移量需要分析 cache_t 大小:16字节
- 0x20是一种整型常量的表示方式。以0x开头的整型常量,代表后续字符为16进制表达。于是0x20也就是16进制的20,即10进制的32。另外,0x20作为单字节表示,可以用于字符型变量的赋值,用于char时,其代表ascii码值0x20,即字符空格’ ‘。
好了,前期的准备工作做完了,让我们按步分析 bits 中的内容吧:
bits探索
1、根据偏移获取class_data_bits_t
类型的 bits
x/4gx test
其中首地址0x100645940
0x100645940
平移32字节为0x100645960
- 因为
bits
是class_data_bits_t
类型, 我们要取地址所以class_data_bits_t *
类型强转一下, 变成指针地址
2、获取 bits 中class_rw_t *
类型的 data
p $2->data()
是因为struct class_data_bits_t
源码(->
是因为当前的是指针, 结构体的话用.
)
3、分别探索 data 下的 prsperties 与 Methods
可看到class_rw_t
(结构体)里面提供一些属性properties
, 方法列表methods
, 协议列表protocols
的方法。
- 打印属性列表
- data 中查找
property_array_t
类型的 属性properties - properties 中查看 property_array_t(以property_t结构体为参) ,再继续查看 property_array_tt
- data 中查找
- 打印方法列表
- data 中查找
method_array_t
类型的 方法methods - methods 中查看 method_array_t(以method_t结构体为参) ,再继续查看 list_array_tt
- data 中查找
- 打印协议列表
协议列表
的list结构, 获取data后 p $3.protocols()
即可
补充
1、objc_object VS NSObject、objc_class VS Class、dispatch_objc VS dispatch,iOS底层实现使用带objc内容进行操作
2、__has_feature(ptrauth_calls)
有些时候__has_feature(ptrauth_calls)
与TARGET_OS_SIMULATOR
一起使用, 需要先普及ARM64e
概念
// ARM64 simulators have a larger address space, so use the ARM64e\
// scheme even when simulators build for ARM64-not-e.
//ARM64模拟器有更大的地址空间,所以使用ARM64e\
//即使在为ARM64-not-e构建模拟器时也是如此。
复制代码
ARM64e
是arm64e架构
,用于Apple A12及更高版本
A系列处理器 或 新型OS_SIMULATOR
模拟器的设备。
__has_feature(ptrauth_calls)
: 是判断编译器是否支持指针身份验证功能ptrauth_calls
指针身份验证,针对arm64e架构;使用Apple A12或更高版本A系列处理器的设备(如iPhone XS、iPhone XS Max和iPhone XR或更新的设备)支持arm64e架构
MachOView(反编译)
Symbol Table
→ Symbols
中可看到, 实际在底层多了个_OBJC_METACLASS
(meta class : 元类)
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END