项目中有,先记点runtime:
一. runtime基础
1.objc_object,objc_class,meta class,objc_method,objc_selector,SEL,IMP,method_types
首先oc中所有实例的本质其实是一个结构体指针,叫做objc_object,里面只有一个属性,叫isa指针,他是一个叫指向做objc_class的结构体指针。
struct objc_object {
Class _Nonnull isa; // objc_object 结构体的实例指针
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
struct objc_class {
Class _Nonnull isa; // objc_class 结构体的实例指针
#if !__OBJC2__
Class _Nullable super_class; // 指向父类的指针
const char * _Nonnull name; // 类的名字
long version; // 类的版本信息,默认为 0
long info; // 类的信息,供运行期使用的一些位标识
long instance_size; // 该类的实例变量大小;
struct objc_ivar_list * _Nullable ivars; // 该类的实例变量列表
struct objc_method_list * _Nullable * _Nullable methodLists; // 方法定义的列表
struct objc_cache * _Nonnull cache; // 方法缓存
struct objc_protocol_list * _Nullable protocols; // 遵守的协议列表
#endif
};
所以定义了一个实例,其中的指向关系是,objc_object中的isa指向objc_class,objc_class的isa指向metaclass(元类)
objc_class中的methodlist是一个数组,存储的是这个类当中的方法的地址
每个方法的类型叫做objc_method
typedef struct objc_method *Method;
struct objc_method {
SEL _Nonnull method_name; // 方法名
char * _Nullable method_types; // 方法类型
IMP _Nonnull method_imp; // 方法实现
};
/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
/// A pointer to the function of a method implementation.
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...);
#endif
SEL
是一个指向 objc_selector 结构体
的指针,但是在 runtime 相关头文件中并没有找到明确的定义。不过,通过测试我们可以得出: SEL
只是一个保存方法名的字符串。
IMP的实质是一个函数指针,所指向的就是方法的实现。IMP
用来找到函数地址,然后执行函数。
二.消息转发
oc中的方法查找过程:首先查objc_object的isa,然后查objc_class的缓存cache,没有就查methodlist,没有再查父类,以此类推,如果查到最后都没找到,那么就是接下来要说的消息转发机制。
调用 [receiver selector];
后,进行的流程:
- 编译阶段:
[receiver selector];
方法被编译器转换为:objc_msgSend(receiver,selector)
(不带参数)objc_msgSend(recevier,selector,org1,org2,…)
(带参数)
- 运行时阶段:消息接受者
recever
寻找对应的selector
。- 通过
recevier
的isa 指针
找到recevier
的class(类)
; - 在
Class(类)
的cache(方法缓存)
的散列表中寻找对应的IMP(方法实现)
; - 如果在
cache(方法缓存)
中没有找到对应的IMP(方法实现)
的话,就继续在Class(类)
的method list(方法列表)
中找对应的selector
,如果找到,填充到cache(方法缓存)
中,并返回selector
; - 如果在
class(类)
中没有找到这个selector
,就继续在它的superclass(父类)
中寻找; - 一旦找到对应的
selector
,直接执行recever
对应selector
方法实现的IMP(方法实现)
。 - 若找不到对应的
selector
,Runtime 系统进入消息转发机制。
- 通过
- 运行时消息转发阶段:
- 动态解析:通过重写
+resolveInstanceMethod:
或者+resolveClassMethod:
方法,利用class_addMethod
方法添加其他函数实现; - 消息接受者重定向:如果上一步添加其他函数实现,可在当前对象中利用
-forwardingTargetForSelector:
方法将消息的接受者转发给其他对象; - 消息重定向:如果上一步没有返回值为
nil
,则利用-methodSignatureForSelector:
方法获取函数的参数和返回值类型。- 如果
-methodSignatureForSelector:
返回了一个NSMethodSignature
对象(函数签名),Runtime 系统就会创建一个NSInvocation
对象,并通过-forwardInvocation:
消息通知当前对象,给予此次消息发送最后一次寻找 IMP 的机会。 - 如果
-methodSignatureForSelector:
返回nil
。则 Runtime 系统会发出-doesNotRecognizeSelector:
消息,程序也就崩溃了。
- 如果
- 动态解析:通过重写
三. method swizzing
这是oc的一种消息交换机制,可以在运行时动态的交换两个方法的实现 。
具体可参考网上资料:iOS 开发:『Runtime』详解(二)Method Swizzling - 掘金