Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
runtime
OC
是动态运行时语言是什么意思?
- 动态类型:运行时确定对象的类型,编译时期能通过,但不代表运行过程中没有问题
- 动态绑定:运行时才确定对象调用的方法(消息转发)
- 动态加载:动态库的方法实现不拷贝到程序中,只记录引用,直到使用相关方法的时候才到库里面查找方法实现
runtime
能做什么?
- 获取类的成员变量、方法、协议
- 为类添加成员变量、方法、协议
- 动态改变方法实现
class_copyIvarList
与class_copyPropertyList
的区别?
- 1、
class_copyIvarList
可以获取.h
和.m
中的所有属性以及@interface
大括号中声明的变量,获取的属性名称有下划线(大括号中的除外)。 - 2、
class_copyPropertyList
只能获取由@property
声明的属性(包括.m
),获取的属性名称不带下划线。
class_ro_t
和class_rw_t
的区别?
class_rw_t
提供了运行时对类拓展的能力,class_rw_t
结构体中存储了class_ro_t
。class_ro_t
存储的是类在编译时已经确定的信息,是不可改变的。- 二者都存有类的方法、属性(成员变量)、协议等信息,不过存储它们的列表实现方式不同。简单的说
class_rw_t
存储列表使用的二维数组,class_ro_t
使用的一维数组。 - 运行时修改类的方法,属性,协议等都存储于
class_rw_t
中
什么是 Method Swizzle(黑魔法),什么情况下会使用?
Method Swizzle
是改变一个已存在的选择器(SEL
)对应的实现(IMP
)的过程。- 类的方法列表存放着
SEL
的名字和IMP
的映射关系。 - 开发者可以利用
method_exchangeImplementations
来交换2个方法中的IMP
- 开发者可以利用
method_setImplementation
来直接设置某个方法的IMP - 这就可以在运行时改变
SEL
和IMP
的映射关系,从而实现方法替换。
Method Swizzle
注意事项
- 为了确保
Swizzle Method
方法替换一定被执行调用,可以在load
中执行 +load
里面使用的时候不要调用[super load]
。如果多次调用了[super load]
,可能会出现“Swizzle无效”的假象- 避免调用
[super load]
导致Swizzling
多次执行,在load
中使用dispatch_once
确保交换只被执行一次。 - 子类替换没有实现的继承方法,会替换掉父类中的实现,影响父类及其他子嘞
+initialize
里面使用要加dispatch_once
- 进行版本迭代的时候需要进行一些检验,防止系统库的函数发生了变化
如何hook
一个对象的方法,而不影响其它对象
- 方法1:新建一个子类重写方法
- 方法2:让这个对象的类遵循某个协议,
hook
时判断。弊端是其他对象遵循了这个协议会受到影响。 - 方法3:运行时创建一个新的子类,修改对象
isa
指针指向子类,hook
时使用isKindOf
判断类型
消息发送
消息机制
- 1、快速查找,方法缓存
- 2、慢速查找,方法列表
- 3、消息转发
- 3-1、方法的动态解析,
resolveInstanceMethod
- 3-2、快速消息转发,
forwardingTargetForSelector
- 3-3、标准消息转发,
methodSignatureForSelector & forwardInvocation
- 3-1、方法的动态解析,
objc中向一个nil对象发送消息将会发生什么?
- 在寻找对象的
isa
指针时,返回地址0x0
,不回做任何操作,也不会有任何错误。
objc在向一个对象发送消息时,发生了什么?
- 方法调用实际上是发送消息,通过调用
objc_msgSend()
实现的。 - 首先,通过
obj
的isa
指针找到对应的class
。 - 然后,开启快速查找流程。在
class
的缓存方法列表(objc_cache
)里查找方法,如果找到就直接返回对应IMP
。 - 如果在缓存中找不到,开始慢速查找流程。在
class
的Method List
查找对应方法,找到了返回对应IMP
。 - 都找不到就会走消息转发流程
_objc_msgForward
函数是做什么的?
_objc_msgForward
用于消息转发:向一个对象发送一条消息,但它并没有实现的时候,就调用_objc_msgForward
尝试做消息转发。
为什么需要做方法缓存?
- 每次执行这个方法的时候都查一遍
Method List
太消耗性能。 - 使用
objc_cache
把调用过的方法做一个缓存, 把method_name
作为key
,method_IMP
作为value
。 - 下次接收到消息的时候,直接通过
objc_cache
去找到对应的IMP
即可, 避免每一次都去遍历objc_method_list
一直都找不到方法怎么办?
- 会触发消息转发机制,我们一共有三次机会补救以防止
crash
- 方法的动态解析,通过
resolveInstanceMethod
添加一个IMP使其执行。 - 快速消息转发,在
forwardingTargetForSelector
返回一个可以执行该方法的对象。 - 标准消息转发,
methodSignatureForSelector
创建相同方法类型的方法签名(NSMethodSignature
),然后重写forwardInvocation
并把拥有该签名的方法赋值到anInvocation.selector
。
消息转发机制的优劣
- 优点:消息转发机制提供了找不到方法时的补救机会。
- 缺点:一般情况下会在基类做crash处理,那么有可能把一部分的crash忽略过去导致无法暴露问题。
IMP
、SEL
、Method
的区别和使用场景
SEL
相当于一个代号,方便查找方法的代号,处理通知/定时器等都会用到IMP
是指向方法实现的指针,动态方法解析的时候会用到Method
是一个对象,里面就存有SEL
和IMP
,消息转发流程获取方法签名的时候会用到