1.背景、定义
1.1 Apple开源的Runtime代码
1.2 Objective-C会尽可能的将编译要做的事情推迟到运行时。 Objective-C 语言不仅需要一个编译环境,同时也需要一个运行时系统(runtime)来执行编译好的代码。
1.3 对于下面的简单方法:
[receiver message];
编译器将会转化为:
objc_msgSend(receiver, selector)//无参数
objc_msgSend(receiver, selector, arg1, arg2, ...)//有参数
2.消息传递流程
① 检查receiver是否为nil,如果receiver为空,则会将 selector也设置为空,直接返回到消息调用的地方,否则下一步
② 根据SEL到当前类中查找对应的IMP,先会在cache中检索它,如果找到了就根据函数指针跳转到这个函数执行,否则下一步
③ 检索当前类对象中的方法表(method list),如找到了,加入cache中,并且就跳转到这个函数之行,否则下一步
④ 从父类中寻找,直到根类:NSObject类。找到了就将方法加入对应类的cache表中,如果找不到则进行动态方法决议(3),仍解决不了则下一步
⑤ 进入消息转发(4),仍解决不了则下一步
⑥ 程序崩溃
3.动态方法
+ (BOOL)resolveClassMethod:(SEL)sel __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ (BOOL)resolveInstanceMethod:(SEL)sel __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
这个函数分别添加实例方法实现和类方法实现
在运行时(runtime),没有找到SEL的IML时就会执行。这个函数是给类利用class_addMethod添加函数的机会。
如果实现了添加函数代码则返回YES,未实现返回NO。
4.消息转发
在消息转发之前有一次重定向的问题
- (id)forwardingTargetForSelector:(SEL)aSelector __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
流程到了这里,系统给了个将这个SEL转给其他对象的机会。
返回参数是一个对象,如果这个对象非nil、非self的话,系统会将运行的消息转发给这个对象执行。否则,继续下一步。
- (void)forwardInvocation:(NSInvocation *)anInvocation;
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
5.runtime方法
两个可能会会在这个流程需要的方法:
OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
6.注解
①SEL:方法选择器,在objc.h的定义为:
typedef struct objc_selector *SEL;
所以SEL是char*指针,代表它的方法名称
②IMP:函数指针
7.推荐
https://github.com/nicklockwood/NullSafe
8.感谢