先上图:

下面根据具体代码看这张图。

一、创建一个Person类,

Person.h

#import <Foundation/Foundation.h>

@interface Person : NSObject

-(void)sendMessage:(NSString *)message;

@end

Person.m

#import "Person.h"
#import <objc/runtime.h> @implementation Person @end

大家可以看到,Person类只声明了 sendMessage:方法,在.m文件里没有实现这个方法。

这时,如果在viewController中调用Person类的sendMessage方法,程序会发生崩溃。

#import "ViewController.h"
#import "Person.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad]; [[[Person alloc]init] sendMessage:@"Hello"]; }

结合上面的图片,我们说说消息处理的机制。

1.当我们调用的方法没有具体的实现时,会调用

+ (BOOL)resolveInstanceMethod:(SEL)sel;

+(BOOL)resolveInstanceMethod:(SEL)sel{

    NSString *methodName = NSStringFromSelector(sel);
if ([methodName isEqualToString:@"sendMessage:"]) {
//我们可以在这里添加方法的实现
return class_addMethod(self, sel, (IMP)sendMessage, "v@:@");
} return NO; } void sendMessage (id self, SEL _cmd, NSString *message){
NSLog(@"message=%@",message);
}

BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types):为类动态添加方法。如果有同名会返回NO,成功返回YES。

其中的参数types查询地址:(v:表示void, @:表示类型,等等

2. 如果 resolveInstanceMethod:方法返回NO,调用

-(id)forwardingTargetForSelector:(SEL)aSelector;

这个方法是找备用者,比如:Animal类。

Animal.h

#import <Foundation/Foundation.h>

@interface Animal : NSObject

@end

Animal.m

#import "Animal.h"

@implementation Animal

-(void)sendMessage:(NSString *)message{
NSLog(@"message=%@",message);
} @end

Animal类没有声明sendMessage:方法,但在.m文件里有这个方法的实现,可以作为备用者。如下:

-(id)forwardingTargetForSelector:(SEL)aSelector{

    NSString *methodName = NSStringFromSelector(aSelector);
if ([methodName isEqualToString:@"sendMessage:"]) {
if ([[Animal new] respondsToSelector:aSelector]) {
return [Animal new];
}
} return [super forwardingTargetForSelector:aSelector];
}

3. 如果 forwardingTargetForSelector:(SEL)aSelector返回 nil。

// 若前两种方法都不处理,则走这里
// 1)方法签名
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{ NSString *methodName = NSStringFromSelector(aSelector);
if ([methodName isEqualToString:@"sendMessage:"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
return [super methodSignatureForSelector:aSelector];
}
// 2) 签名后,消息转发,找备用者
-(void)forwardInvocation:(NSInvocation *)anInvocation{ SEL selector = [anInvocation selector];
Animal *animal = [Animal new];
if ([animal respondsToSelector:selector]) {
[anInvocation invokeWithTarget:animal];
} else{
[super forwardInvocation:anInvocation];
}
}

4.如果走到第3步,仍然不做处理,如下:

-(void)forwardInvocation:(NSInvocation *)anInvocation{

    [super forwardInvocation:anInvocation];
}

这时为了程序的健壮性,防止崩溃,可以用以下方法处理。

//  若前3方法都不处理,为了防止崩溃,可调用此方法
-(void)doesNotRecognizeSelector:(SEL)aSelector{
NSString *methodName = NSStringFromSelector(aSelector);
NSLog(@"找不到 %@ 这个方法的实现",methodName);
}

附加源码

Runtime消息动态解析与转发流程的更多相关文章

  1. Objective-C RunTime 学习笔记 之 消息转发流程

    1) 当向某个对象发送消息时,先从cache(cache_t)中查找方法对象(method_t),如果找到则进行回调:否则通过查找对象的类(元类)定义中方法列表,一直追溯到NSObject, 如果找到 ...

  2. runtime消息转发机制

    Objective-C 扩展了 C 语言,并加入了面向对象特性和 Smalltalk 式的消息传递机制.而这个扩展的核心是一个用 C 和 编译语言 写的 Runtime 库.它是 Objective- ...

  3. Protobuf动态解析那些事儿

    需求背景 在接收到 protobuf 数据之后,如何自动创建具体的 Protobuf Message 对象,再做反序列化.“自动”的意思主要有两个方面:(1)当程序中新增一个 protobuf Mes ...

  4. Runtime - 消息发送原理

    Runtime - 消息发送原理. Objective-C运行时的核心就在于消息分派器objc_msgSend,消息分派器把选择器映射为函数指针,并调用被引用的函数. 要想理解objc_msgSend ...

  5. Protobuf动态解析在Java中的应用 包含例子程序

    最近在做ProtoBuf相关的项目,其中用到了动态解析,网上看了下相关资料和博文都比较少,自己来写一个记录一下学习过程.   Protocol Buffers是结构化数据格式标准,提供序列化和反序列方 ...

  6. Sentinel源码解析一(流程总览)

    引言 Sentinel作为ali开源的一款轻量级流控框架,主要以流量为切入点,从流量控制.熔断降级.系统负载保护等多个维度来帮助用户保护服务的稳定性.相比于Hystrix,Sentinel的设计更加简 ...

  7. 使用Newtonsoft.Json.dll(JSON.NET)动态解析JSON、.net 的json的序列化与反序列化(一)

    在开发中,我非常喜欢动态语言和匿名对象带来的方便,JSON.NET具有动态序列化和反序列化任意JSON内容的能力,不必将它映射到具体的强类型对象,它可以处理不确定的类型(集合.字典.动态对象和匿名对象 ...

  8. 理解AngularJS生命周期:利用ng-repeat动态解析自定义directive

    ng-repeat是AngularJS中一个非常重要和有意思的directive,常见的用法之一是将某种自定义directive和ng-repeat一起使用,循环地来渲染开发者所需要的组件.比如现在有 ...

  9. 开源一个动态解析protobuf的工具

    好久没写博客了,主要是这一年技术没啥长进都打杂了,还有就是生活琐事越来越多,人也越来越懒了…… 之前项目中用到了Protobuf,然后测试发现这玩意不好测,总不能每次定个协议或者改下都要编译Java代 ...

随机推荐

  1. 使用 maven-assembly-plugin 打包项目

    此种方式可避免resource节点对compile阶段的影响,compile阶段会读取resource节点的信息但是不会读取assembly的配置文件 1. pom文件 <?xml versio ...

  2. centos7上安装nagios

    前言:Nagios是一款开源的免费网络监视工具,可以监控Windows.Linux和Unix的主机状态,交换机路由器等网络设备,在系统或服务状态异常时发出邮件或短信报警,第一时间通知网站运维人员.流量 ...

  3. 从github clone文件: Failed to receive SOCKS4 connect request ack.

    安装了代理,能上网,也能从github上下载文件,就是无法从github上clone文件, 查了很久资料后,终于发现使用sudo可以解决问题.不过,不知道原因是什么? 比如:git clone htt ...

  4. mysql 开发进阶篇系列 39 mysql日志之二进制日志(binlog)

    一.概述 二进制日志(binlog)记录了所有的DDL(数据定义语言)语句和DML(数据操纵语言)语句,但是不包括数据查询语句, 语句以"事件"的形式保存,它描述了数据的更改过程, ...

  5. hashCode()方法和equal()方法的区别

    本文参考地址:http://www.cnblogs.com/zgq0/p/9000801.html hashCode()方法和equal()方法的作用其实一样,在Java里都是用来对比两个对象是否相等 ...

  6. SpringCloud学习1-服务注册与发现(Eureka)

    由于样式兼容性问题,本文后半部分被截断,可到个人博客找到本文: https://blog.rmiao.top/springcloud-eureka/ 前言 Oracle转让Java,各种动态语言的曝光 ...

  7. 关于springmvc json交互产生的406错误

    产生错误的背景:springmvc使用<mvc:annotation-driven>进行配置,那么只要引入响应的json解析包就可以了.在pom中已经引入了如下: <dependen ...

  8. 自己动手实现java数据结构(一) 向量

    1.向量介绍 计算机程序主要运行在内存中,而内存在逻辑上可以被看做是连续的地址.为了充分利用这一特性,在主流的编程语言中都存在一种底层的被称为数组(Array)的数据结构与之对应.在使用数组时需要事先 ...

  9. 改BUG方法

    ---恢复内容开始--- 今天改了半天参数,后来发现原来是表设置错了,于是决定总结出遇到BUG时的方法,等下次再遇到BUG时,过一遍每个方法就会减少解决BUG的时间,话不多说,写 ①检查表.数据库是否 ...

  10. [USACO13DEC] Optimal Milking

    Description n个点排成一排,点有点权,要求支持两种操作: 修改某个点的点权 询问取出任意多且不相邻的点的点权和最大值 Solution 跟最大子段和一样,可以用分治做,用线段树记录一下左右 ...