iOS消息转发小记
消息转发流程图
如果类接收到无法处理的消息,会触发消息转发机制,一共有三个步骤,接受者在每一步中均有机会处理消息。步骤越往后,处理消息的代价就越大,所以最好再第一步就处理完。
第一道防线
在类里面实现两个方法来处理未知消息。执行动态方法解析之前,先会判断是否曾经有动态解析。
-
resolveInstanceMethod
:处理实例方法 -
resolveClassMethod
:处理类方法
我们来看个Demo,先看调用方代码
TestA *testA = [[TestA alloc] init]; [testA instanceMethod]; [TestA classMethod];
再来看看TestA的定义。
// TestA.h @interface TestA : NSObject - (void)instanceMethod; + (void)classMethod; @end // TestA.m @implementation TestA - (void)newInstanceMethod { NSLog(@"newInstanceMethod"); } + (void)newClassMethod { NSLog(@"newClassMethod"); } + (BOOL)resolveInstanceMethod:(SEL)sel { if (sel == @selector(instanceMethod)) { // 动态添加方法newInstanceMethod Method method = class_getInstanceMethod([self class], @selector(newInstanceMethod)); IMP imp = method_getImplementation(method); class_addMethod([self class], sel, imp, method_getTypeEncoding(method)); // 成功处理,消息转发机制结束,调用newInstanceMethod return YES; } // 不能处理,进入第二步 return [super resolveInstanceMethod:sel]; } + (BOOL)resolveClassMethod:(SEL)sel { if (sel == @selector(classMethod)) { // 动态添加方法newClassMethod Method method = class_getInstanceMethod(object_getClass(self), @selector(newClassMethod)); IMP imp = method_getImplementation(method); class_addMethod(object_getClass(self), sel, imp, method_getTypeEncoding(method)); // 成功处理,消息转发机制结束,调用newClassMethod return YES; } // 不能处理,进入第二步 return [super resolveClassMethod:sel]; } @end
TestA中头文件定义了两个方法,但是没有实现,如果不用消息转发机制处理异常,会导致crash,log想必大家应该很熟悉
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[TestA funcA]: unrecognized selector sent to instance 0x6040000125c0'
实例方法存储在类对象,类方法存储在元类对象,在调用class_addMethod
时,第一个参数需要注意。
第二道防线
第二道防线依赖一个函数forwardingTargetForSelector
。
// 类方法 //+ (id)forwardingTargetForSelector:(SEL)aSelector { // //} - (id)forwardingTargetForSelector:(SEL)aSelector { if (aSelector == @selector(instanceMethod)) { // 消息转发给TestB实例 return [TestB new]; } // 消息转发失败,进入下一步 return nil; } // TestB.m - (void)instanceMethod { NSLog(@"instanceMethod"); }
第三道防线
第三道防线有两步
- 调用
methodSignatureForSelector
,获取新的方法签名(返回值类型,参数类型) - 调用
forwardInvocation
,转发消息,
// 方法签名(返回值类型,参数类型) // 类方法减号改为加号 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { NSMethodSignature *signature = [TestB instanceMethodSignatureForSelector:aSelector]; return signature; } // NSInvocation封装了方法调用,包括:方法调用者、方法名、方法参数 // anInvocation.target 消息接受者 // anInvocation.selector 函数名 // [anInvocation getArgument:NULL atIndex:0]; 获取参数 // 类方法减号改为加号 - (void)forwardInvocation:(NSInvocation *)anInvocation { [anInvocation invokeWithTarget:[TestB new]]; }

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Android中常用的加密方式
Android面试的时候,尤其是面试一些金融公司的时候经常性的问道:“你会不会加密?”,“加密方式是什么?”,“大概什么样的加密原理呢?”。其实,大多数人都是一脸懵逼,我也懵逼过。痛下决心总结一下,写的不好,大家见笑了。 一、Rsa加密 1、RSA是第一种既可以用于数据加密,也可以用于数字签名的算法; 2、算法原理: 1)、随机产生两个大的质数m、n且m!=n,计算K1=mn; 2)、选择一个大于1小于k1的自然数k2,k2必须与(m-1)(n-1)互为素数; 3)、计算得到d--->d x k2=1(mod(m-1)(n-1)); 4)、销毁mn; 最终产生的k1和k2为“公钥”,d为“私钥”,发送方使用k1进行加密,接收方使用d进行解密。 3、注意: 1、RSA的安全性依赖于大数分解,小于1024位的k1被认为是不安全的; 2、RSA的计算速度慢。 4、使用 1、生成密钥对 /** * 随机生成RSA密钥对 * * @param keyLength 密钥长度,范围:512~2048 * 一般1024 * @return */ public static KeyPair gen...
- 下一篇
Android Handler机制原理及源码解析
今天打算写一下Handler机制的原理及源码解析,Handler在我们的开发中用到的频率还是非常高的,同时这也是一个非常基础的知识点,但是即使是基础知识,有很多工作两三年的安卓开发依然是一知半解,搞不清楚原理,包括View、ViewGroup的事件分发及绘制流程。 在深入学习一下知识点之前,希望能够带着疑问去思考: 1.为什么在子线程实例化Handler会报错闪退,而主线程不会 2.为什么每个线程只能存在一个Looper和MessageQueue 3.多个Handler发送消息是怎么保证Looper轮询消息队列发送最新消息不错乱发给其他Handler的 4.子线程真的不能更新UI吗? 5.ThreadLocal的作用 ...... Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { Log.e("接收消息", (String) msg.obj); } }; @Override protected void onCreate(Bundle savedInstanceStat...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7设置SWAP分区,小内存服务器的救世主
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- Windows10,CentOS7,CentOS8安装Nodejs环境
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS8安装Docker,最新的服务器搭配容器使用
- 设置Eclipse缩进为4个空格,增强代码规范
- Docker快速安装Oracle11G,搭建oracle11g学习环境