Objective-C Runtime 解读 (二)
来源:互联网 发布:we淘宝官方旗舰店 编辑:程序博客网 时间:2024/06/03 18:17
Objective-C Runtime 解读 (二)
关于runtime的解读, 前一章节主要讲解了基本的概念, 其实runtime的运用是无处不在的, 本章节主要解读runtime在”消息转发机制”中的体现.
消息发送的理解
其实, 我们一般所说的函数调用, 在OC中我们更习惯叫做消息发送, 一般会这样写: [someObj dosomething], 这种[ ]的写法, 其实就是消息发送, 其实最终是转换成了下面的形式:
objc_msgSend(someObj,@selector(dosomething));
有参数的是这样:
objc_msgSend(someObj,@selector(dosomething:),var1);
看到这里, 其实我们就已经很清楚, 为什么说OC是基于消息发送的机制, 其实底层也就是调用了C函数.
消息发送机制
可能我们经常遇到下面的崩溃信息:
2016-04-01 11:41:03.867 RunTimeTestDemo[19405:1760968] -[ViewController clickAction:]: unrecognized selector sent to instance 0x7fff33c276002016-04-01 11:41:03.874 RunTimeTestDemo[19405:1760968] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[ViewController clickAction:]: unrecognized selector sent to instance 0x7fff33c27600'
出现这种情况就是, 我们发送的消息没有被正确解析出来, 那么, 接下来就详细介绍OC中的动态消息转发机制.
其实消息的转发可以大致分为两个阶段, 一个是动态方法解析, 而是完整的消息转发, 那么接下来先看第一个阶段:
动态方法解析
在这里, 就不得不提到OC的强大之处, 因为OC是动态语言, 因此消息的解析是运行时完成的, 还记得前一章节提到的isa指针, 那么动态方法解析的时候就需要使用到isa指针了.
首先消息发送之后, 先查询接受者, 也就是通过isa指针查找相对应的method_list分发表, 如果没有查找到指定的selector, 那么会接着延superClass继续查找, 直到查到根类, 如果这个过程中该选择子可以被执行了, 那么动态解析也就结束了, 如果不可以, 那么接下来就看当前类能否动态添加方法, 来处理这个未知的选择子了.
对象收到无法解读的消息之后, 会调用所属类的下列方法:
//实力方法+ (BOOL)resolveInstanceMethod:(SEL)sel//类方法+ (BOOL)resolveClassMethod:(SEL)sel
在这里其实我们还是可以处理这个未知的选择子, 通过动态添加方法的形式, 处理未知的选择子, 这也是第一个阶段, 到最后runtime机制给我们提供的处理未知选择子的方法.
大致可以这样解决:
void addMethod(id obj, SEL _cmd){ NSLog(@"Doing add method"); NSLog(@"%@", obj); NSLog(@"%@", NSStringFromSelector(_cmd));}+ (BOOL)resolveInstanceMethod:(SEL)sel{ if (sel == @selector(clickAction:)) { class_addMethod([self class],sel,(IMP)addMethod,"v@:"); return YES; } return [super resolveInstanceMethod:sel];}
通过动态添加方法的形式来处理这个未知的选择子, 到这里第一阶段的工作也就基本结束了, 在第一阶段中, 其实我们已经可以处理未知的选择子了, 当然如果没有在第一阶段处理掉, 其实runtime还是给了我们第二次处理的机会, 接下来就说说完整的消息转发阶段:
完整的消息转发
其实,这个阶段也可以分为两小节来看待, 首先就是实现备援接受者, 这个其实跟前一个阶段的实现形式差不多, 大致实现如下:
- (id)forwardingTargetForSelector:(SEL)aSelector{ TargetObject *object = [TargetObject new]; if (aSelector == @selector(clickAction:) && [object respondsToSelector:aSelector]) { return object; } return [super forwardingTargetForSelector:aSelector];}
这种处理方式, 其实更像向备援接受者”借用”了这个方法, 其实仔细想想, 这个跟继承的概念有点像, 但又不一样, 因为当前类没有继承备援类, 但是却可以使用备援类中的方法, 而继承也是可以这样的, 如果当前类有多个备援接受者, 其实就可以近似看成”多继承”类理解.
还有一点需要注意的是, 如果当前类借用了备援接受者中的很多方法来处理未知的选择子, 那么其实就可以直接继承来实现了, 因为通过动态消息解析的方式是比较消耗资源的, 因此, 建议这种情况下, 直接继承实现.
当着一部分还无法处理是, 接下来就需要启动完整的消息转发机制了, 此步骤回调用下列方法转发:
- (void)forwardInvocation:(NSInvocation *)anInvocation
这一阶段首先会创建Invocation对象, 将于未处理选择子相关的信息装在里面, 包括: 选择子, 目标以及参数等, 大致解决方式如下:
//首先需要their method signatures- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{ TargetObject *object = [TargetObject new]; return [object methodSignatureForSelector:aSelector];}//然后, 重定向-(void)forwardInvocation:(NSInvocation *)invocation{ SEL invSEL = invocation.selector; if([altObject respondsToSelector:invSEL]) { [invocation invokeWithTarget:altObject]; } else { [self doesNotRecognizeSelector:invSEL]; }}
这个方法其实比较简单, 只是改变了调用目标, 是的消息在新目标上执行, 这其实跟备援接受者的实现方式类似, 都是讲未知的选择子转给可以处理的对象来处理.
下面这张图描述了消息转发机制处理消息的每个步骤:
该图选自Effective Objective-C 2.0 这本书, 建议大家阅读一下, 里面有很多高效开发的知识点, 还有一些很深入的讲解.
上图中可以看出, 在整个消息转发机制中, 每个阶段都可以处理未知的选择子, 但是步骤越往后, 处理消息的代价就越大, 最好是能够在第一步就处理掉, 这样的话, 其实代价相对较小.
总结
本文主要讲解了OC中动态消息转发的各个阶段, 也主要体现了runtime机制的强大之处, 通过运行期的动态方法解析, 我们可以动态添加需要的方法到类中, 也可以使用备援接受者来处理, 最后还可以通过完整的消息转发机制, 来处理未知消息.
因此, runtime机制使得我们的程序更健壮, 而且更灵活, 可以再运行期做更多的事情.
参考:
Objective-C Runtime Programming Guide
理解 Objective-C Runtime
- Objective-C Runtime 解读 (二)
- Objective-C Runtime 解读 (一)
- Objective-C Runtime (二)
- Objective-C runtime之消息(二)
- Objective-C runtime之消息(二)
- Objective-C runtime之消息(二)
- Objective-C runtime之消息(二)
- Objective-C runtime之消息(二)
- Objective-C runtime之消息(二)
- Objective-C runtime之消息(二)
- Objective-C Runtime深入理解(二)
- Runtime of Objective-C
- Runtime of Objective-C
- objective-c runtime
- Objective-C Runtime
- 详解Objective-C runtime
- 详解Objective-C runtime
- 详解Objective-C runtime
- 8.6 tempfile--临时文件和目录处理
- 启发式算法-A*算法
- poj 2549 Sumsets (枚举+二分)
- angular中的module和injector,即依赖注入
- HD 1213 How Many Tables(裸 并查集)
- Objective-C Runtime 解读 (二)
- 反应式宣言
- java项目——发邮件之阿里云邮箱推送服务(二)
- android自定义View之钟表诞生记
- jquery部分笔记
- Android 布局属性大全
- day25
- 算法初级_2 :数组和指针
- 带图形验证码的登录模块