iOS的消息转发机制详解
iOS开发过程中,有一类的错误会经常遇到,就是找不到所调用的方法,当然这类问题比较好解决,给当前对象或其父类对象添加该方法即可,使得编译器在编译时能正确找到该方法;或者,还有另外的方法,由于Objective-C是一门动态语言,我们也可以在运行期再给类添加该方法,一样可以解决该问题,而这就涉及了类的消息转发机制。
本文就主要来介绍一下iOS系统的消息转发机制,探究一下在调用一个方法时,如果本类中没有该方法时,对象究竟是如何进行消息转发的,来避免程序抛出异常。
异常现象
当调用的对象方法不存在,即使经过消息转发也不存在时,就会抛出下面的异常
-[Teacher playPiano]: unrecognized selector sent to instance 0x6000000114c0
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Teacher playPiano]: unrecognized selector sent to instance 0x6000000114c0'
解决办法
针对上述的异常问题,最简单的方法就是直接在类中添加playPiano方法,或者在其继承树中添加该方法,均可以解决该问题,所以这种方法再次不再赘述,下面介绍一下如何利用消息转发机制解决该问题。
消息转发是在运行时进行的,大致分为两个阶段:第一阶段是先检查接收者,看是否能通过runtime动态添加一个方法,来处理这个unknown selector的消息;第二阶段就是完整的消息转发机制,首先会先查看有没有其它对象能够处理该消息,如果没有,就把该消息的全部信息封装到NSInvocation对象中,看那个对象能否处理,如果还无法处理,就查看继承树中的类是否能够处理该消息,如果到NSObject之前都无法处理该消息,那么最后就会调用NSObject类的doesNotRecognizeSelector方法来抛出异常,表明调用的方法不存在。
1.动态方法解析
对象在收到无法处理的消息时,会调用下面的方法,前者是调用类方法时会调用,后者是调用对象方法时会调用
// 类方法专用
+ (BOOL)resolveClassMethod:(SEL)sel
// 对象方法专用
+ (BOOL)resolveInstanceMethod:(SEL)sel
在该方法中,需要给对象所属类动态的添加一个方法,并返回YES,表明可以处理
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSString *method = NSStringFromSelector(sel);
if ([@"playPiano" isEqualToString:method]) {
/**
添加方法
@param self 调用该方法的对象
@param sel 选择子
@param IMP 新添加的方法,是c语言实现的
@param 新添加的方法的类型,包含函数的返回值以及参数内容类型,eg:void xxx(NSString *name, int size),类型为:v@i
*/
class_addMethod(self, sel, (IMP)playPiano, "v");
return YES;
}
return NO;
}
2.备援接受者
经历了第一步后,如果该消息还是无法处理,那么就会调用下面的方法,查询是否有其它对象能够处理该消息
- (id)forwardingTargetForSelector:(SEL)aSelector
在这个方法里,我们需要返回一个能够处理该消息的对象
- (id)forwardingTargetForSelector:(SEL)aSelector
{
NSString *seletorString = NSStringFromSelector(aSelector);
if ([@"playPiano" isEqualToString:seletorString]) {
Student *s = [[Student alloc] init];
return s;
}
// 继续转发
return [super forwardingTargetForSelector:aSelector];
}
3.完整的消息转发
经历了前两步,还是无法处理消息,那么就会做最后的尝试,先调用methodSignatureForSelector:获取方法签名,然后再调用forwardInvocation:进行处理,这一步的处理可以直接转发给其它对象,即和第二步的效果等效,但是很少有人这么干,因为消息处理越靠后,就表示处理消息的成本越大,性能的开销就越大。所以,在这种方式下,会改变消息内容,比如增加参数,改变选择子等等。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
- (void)forwardInvocation:(NSInvocation *)anInvocation
下面是改变选择子的例子,比如我们直接调用的是playPiano方法,最后转发给了traval:方法,完整实例参考:MsgSendDemo
// 完整的消息转发
- (void)travel:(NSString*)city
{
NSLog(@"Teacher travel:%@", city);
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSString *method = NSStringFromSelector(aSelector);
if ([@"playPiano" isEqualToString:method]) {
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:@"];
return signature;
}
return nil;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
SEL sel = @selector(travel:);
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:@"];
anInvocation = [NSInvocation invocationWithMethodSignature:signature];
[anInvocation setTarget:self];
[anInvocation setSelector:@selector(travel:)];
NSString *city = @"北京";
// 消息的第一个参数是self,第二个参数是选择子,所以"北京"是第三个参数
[anInvocation setArgument:&city atIndex:2];
if ([self respondsToSelector:sel]) {
[anInvocation invokeWithTarget:self];
return;
} else {
Student *s = [[Student alloc] init];
if ([s respondsToSelector:sel]) {
[anInvocation invokeWithTarget:s];
return;
}
}
// 从继承树中查找
[super forwardInvocation:anInvocation];
}
iOS的消息转发机制给我们提供了更多的选择,来保证消息的正常传递,而了解这些具体的实现方法,则可以让我们的程序更加的健壮。
参考资料
NSMethodSignature和NSInvocation的用法
使用NSMethodSignature和NSInvocation实现消息转发
iOS的消息转发机制详解的更多相关文章
- IOS 触摸事件分发机制详解
欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~ 作者:MelonTeam 前言 很多时候大家都不关心IOS触摸事件的分发机制的实现原理,当遇到以下几种情形的时候你很可能抓破头皮都找不到解决方案 ...
- iOS 消息转发机制
这篇博客的前置知识点是 OC 的消息传递机制,如果你对此还不了解,请先学习之,再来看这篇.这篇博客我尝试用口语的方式像讲述 PPT 一样给大家讲述这个知识点. 我们来思考一个问题,如果对象在收到无法解 ...
- iOS runtime探究(二): 从runtime開始深入理解OC消息转发机制
你要知道的runtime都在这里 转载请注明出处 http://blog.csdn.net/u014205968/article/details/67639289 本文主要解说runtime相关知识, ...
- iOS Runtime的消息转发机制
前面我们已经讲解Runtime的基本概念和基本使用,如果大家对Runtime机制不是很了解,可以先看一下以前的博客,会对理解这篇博客有所帮助!!! Runtime基本概念:https://www.cn ...
- iOS消息转发机制
iOS消息转发机制 “消息派发系统”(message-dispatch system) 若想令类能够理解某条消息,我们必须实现出对应的方法才行.但是,在编译器向类发送其无法解读的消息时并不会报错,因为 ...
- MVVMLight消息通知实现机制详解(二)
接上文 MVVMLight消息通知实现机制详解(一) 该工具的内部主要逻辑是以字典模式进行储存持有订阅对象设置的传入参数Type类型.Key值.Action.Target(订阅对象本身) 在发生订阅事 ...
- MVVMLight消息通知实现机制详解(一)
最近对委托.事件的订阅使用的太多,订阅与被订阅之间的绑定约束非常...麻烦,所以翻了下MVVMLight源码找出这段可以拿出来用的部分,详情见下: 一.开发中遇到的问题: 场景1:ClassA中存在事 ...
- iOS中MVC等设计模式详解
iOS中MVC等设计模式详解 在iOS编程,利用设计模式可以大大提高你的开发效率,虽然在编写代码之初你需要花费较大时间把各种业务逻辑封装起来.(事实证明这是值得的!) 模型-视图-控制器(MVC)设计 ...
- 浏览器 HTTP 协议缓存机制详解
最近在准备优化日志请求时遇到了一些令人疑惑的问题,比如为什么响应头里出现了两个 cache control.为什么明明设置了 no cache 却还是发请求,为什么多次访问时有时请求里带了 etag, ...
随机推荐
- Gist - Fetch Usage
Introduction Do you prefer the usage of "ES6 Promise"? If you do, you will like the usage ...
- P3390 【模板】矩阵快速幂
题目背景 矩阵快速幂 题目描述 给定n*n的矩阵A,求A^k 输入输出格式 输入格式: 第一行,n,k 第2至n+1行,每行n个数,第i+1行第j个数表示矩阵第i行第j列的元素 输出格式: 输出A^k ...
- Windows平台下搭建MySQL数据库
1.下载安装MySQL数据库: (1)->我的标签->软件下载->计算机相关专业所用软件---百度云链接下载->mysql-installer-community-5.7.18 ...
- virtualbox 安装centos系统,设置双网卡实现虚拟机上网及主宿互访
写在前面:前两天想玩linux,在VMware中装了centos,进入系统后发现连不上网,搜了下教程,/etc/sysconfig/network-scripts/目录下没有 ifcfg-e*的文件 ...
- 二叉树 java实现
class Node { private int data; // 其他数据 private int otherData; private Node left; private Node right; ...
- 【转载】BAT 批处理脚本教程
来源:http://www.cnblogs.com/glaivelee/archive/2009/10/07/1578737.html BAT 批处理脚本 教程 第一章 批处理基础第一节 常用批处 ...
- 【SpringMVC】静态资源访问的问题
在项目中经常会用到一些静态的资源,而一般我们在配置SpringMVC时会让SpringMVC接管所有的请求(包括静态资源的访问), 那么我们怎样才能最简单的来配置静态资源的访问呢? 一,在web.xm ...
- 各种demo:css实现三角形,css大小梯形,svg使用
各种demo: 1.css实现正方形 思路:width为0:height为0:使用boder-width为正方形的边长的一半,不占任何字节:border-style为固体:border-color为正 ...
- Maven在导入其他项目时报错:Plugin execution not covered by lifecycle configuration
这几天想把Spring 攻略第二版完整的学习下,所以就在网上下载了该教材的源码,寻思边看书边练习!之前有过一些Maven开发的相关经验,觉得Maven在引入jar包上的配置还是很方便的,所以这次源码的 ...
- 解决 CefSharp WPF控件不能使用输入法输入中文的问题(代码已提交到 github)
首先,本文所有 代码已经提交到github,需要的可以直接从github获取:https://github.com/starts2000/CefSharp,希望可以帮助到有需要的朋友们. CEF 简介 ...