IOS 消息转发

最近在看消息转发的资料,发现大部分都是理论知识,很少有完整的代码。现在以代码的形式形象的解释一下:

用Xcode创建一个工程

1.正常方法调用

创建一个类Person 代码如下

Person.h代码如下:

#import <Foundation/Foundation.h>

@interface Person : NSObject

- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithUserName:(NSString *)userName;

- (void)logUserName;

@end

Person.m代码如下:

#import "Person.h"

@interface Person()

@property (nonatomic, copy) NSString *userName;

@end

@implementation Person

- (instancetype)initWithUserName:(NSString *)userName
{
    self = [super init];
    if (self) {
        _userName = [userName copy];
    }
    return self;
}

- (void)logUserName
{
    NSLog(@"userName = %@", self.userName);
}

@end

ViewController.m代码如下:

#import "ViewController.h"
#import "Person.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    Person *person = [[Person alloc] initWithUserName:@"小王"];
    [person logUserName];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

运行工程结果为:

2017-03-01 22:47:07.296 RunTimeDemo[24364:1776826] userName = 小王

结果正常

2. unrecognized selector 情况

把Person.m 的logUserName方法删除,此时Person.m 的代码如下:

#import "Person.h"

@interface Person()

@property (nonatomic, copy) NSString *userName;

@end

@implementation Person

- (instancetype)initWithUserName:(NSString *)userName
{
    self = [super init];
    if (self) {
        _userName = [userName copy];
    }
    return self;
}

@end

运行工程会出现 如下结果

2017-03-01 23:06:25.788 RunTimeDemo[24729:1788071] -[Person logUserName]: unrecognized selector sent to instance 0x608000018e00
2017-03-01 23:06:25.796 RunTimeDemo[24729:1788071] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException‘, reason: ‘-[Person logUserName]: unrecognized selector sent to instance 0x608000018e00‘

假如不在Person类中实现logUsrName这个方法,也可以让工程正常运行,此时就涉及到消息转发,Objective-C运行时会给出三次拯救程序崩溃的机会。

如下:

(1).Method resolution

objc运行时会调用+resolveInstanceMethod:或者 +resolveClassMethod:,让你有机会提供一个函数实现。如果你添加了函数,那运行时系统就会重新启动一次消息发送的过程,否则 ,运行时就会移到下一步,消息转发(Message Forwarding)。

(2).Fast forwarding

如果目标对象实现了-forwardingTargetForSelector:,Runtime 这时就会调用这个方法,给你把这个消息转发给其他对象的机会。 只要这个方法返回的不是nil和self,整个消息发送的过程就会被重启,当然发送的对象会变成你返回的那个对象。否则,就会继续Normal Fowarding。 这里叫Fast,只是为了区别下一步的转发机制。因为这一步不会创建任何新的对象,但下一步转发会创建一个NSInvocation对象,所以相对更快点。

(3).Normal forwarding

这一步是Runtime最后一次给你挽救的机会。首先它会发送-methodSignatureForSelector:消息获得函数的参数和返回值类型。如果-methodSignatureForSelector:返回nil,Runtime则会发出-doesNotRecognizeSelector:消息,程序这时也就挂掉了。如果返回了一个函数签名,Runtime就会创建一个NSInvocation对象并发送-forwardInvocation:消息给目标对象。

下面就验证一下上面所对应的函数的执行顺序:

Person.m的代码如下:

#import "Person.h"

#define LOG_FunctionName  NSLog(@"%s", __func__)

@interface Person()

@property (nonatomic, copy) NSString *userName;

@end

@implementation Person- (instancetype)initWithUserName:(NSString *)userName
{
    self = [super init];
    if (self) {
        _userName = [userName copy];
    }
    return self;
}

+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    LOG_FunctionName;
    return YES;
}

- (id)forwardingTargetForSelector:(SEL)aSelector
{
    LOG_FunctionName;
    return [super forwardingTargetForSelector:aSelector];
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    LOG_FunctionName;
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    LOG_FunctionName;
}

@end

运行结果为:

2017-03-01 23:45:21.723 RunTimeDemo[25672:1817388] +[Person resolveInstanceMethod:]
2017-03-01 23:45:21.724 RunTimeDemo[25672:1817388] -[Person forwardingTargetForSelector:]
2017-03-01 23:45:21.724 RunTimeDemo[25672:1817388] -[Person methodSignatureForSelector:]
2017-03-01 23:45:21.724 RunTimeDemo[25672:1817388] +[Person resolveInstanceMethod:]
2017-03-01 23:45:21.725 RunTimeDemo[25672:1817388] -[Person logUserName]: unrecognized selector sent to instance 0x60000001cef0
2017-03-01 23:45:21.729 RunTimeDemo[25672:1817388] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException‘, reason: ‘-[Person logUserName]: unrecognized selector sent to instance 0x60000001cef0‘

运行结果正好验证了上述说明。

时间: 2024-10-21 00:04:23

IOS 消息转发的相关文章

iOS消息转发机制

iOS消息转发机制 “消息派发系统”(message-dispatch system) 若想令类能够理解某条消息,我们必须实现出对应的方法才行.但是,在编译器向类发送其无法解读的消息时并不会报错,因为在运行期可以继续向类中添加方法,所以编译器在编译时还无法确定类中到底会不会有某个方法的实现.当对象接收到无法解读的消息时,就会启动“消息转发”机制,我们可以经由此过程告诉对象应该如何处理未知消息. 消息转发分为两个阶段.第一阶段先征询接收者所属的类,看其是否能动态添加方法,已处理当前这个“未知的选择

iOS消息转发学习笔记

如果深入学习ios Runtime,不得不提到消息转发,很多框架的实现都基于这一功能实现(例如JSPatch) 虽然看了很多篇关于消息转发的文章,但是理解的不是很透彻,还是自己实践一些理解能更加透彻一下. 首先我自己定义了一个MyString继承NSString @interface MyString : NSString @end @implementation MyString @end 然后创建一个MyString,通过performSelector调用MissMethod,MissMet

iOS消息转发

消息转发是一种功能强大的技术,可以大大增加Objective-C的表现力.什么是消息转发?简而言之,它允许未知的消息被困住并作出反应.换句话说,无论何时发送未知消息,它??都会以一个很好的包发送到您的代码中,此时您可以随心所欲地执行任何操作. 为什么它被称为 "转发"? 当某个对象没有任何响应某个 消息 的操作就 "转发" 该 消息.原因是这种技术主要是为了让对象让其他对象为他们处理 消息,从而 "转发". 1. 类,对象,方法 在我们开始使用消

iOS知识树,知识目录(包括对象、Block、消息转发、GCD、运行时、runloop、动画、Push、KVO、tableview,UIViewController、提交AppStore)

本文旨在总结iOS知识网络,该知识网络罗列出常见UIKit,Foundation的对象特点和一些使用经验:文本编辑采用树的形式,对知识点进行罗列,并标注一些使用经验(★)希望对初学者有用或给一些解决疑难杂症者提供思路:某些知识点会深入探讨:通过总结希望站在一个较高平台的角度全观Objective-C.知识树中有些是原创文章,有些则是转载网络上iOS大神的文章.笔者会尽量详细的介绍各个知识点.当然一个人的知识面是相当有限的,在给各位读者提供知识参考的同时,欢迎大家对本文提意见. /->UIView

iOS runtime探究(二): 从runtime開始深入理解OC消息转发机制

你要知道的runtime都在这里 转载请注明出处 http://blog.csdn.net/u014205968/article/details/67639289 本文主要解说runtime相关知识,从原理到实践.由于包括内容过多分为下面五篇文章详细解说.可自行选择须要了解的方向: 从runtime開始: 理解面向对象的类到面向过程的结构体 从runtime開始: 深入理解OC消息转发机制 从runtime開始: 理解OC的属性property 从runtime開始: 实践Category加入属

iOS开发-消息转发

消息转发是OC运行时比较重要的特性,Objective-C运行时的主要的任务是负责消息分发,我们在开发中"unrecognized selector sent to instance xx",实例对象没有实现对应的消息,通常我们只需要实现未实现的方法即可.一般情况我们处理一个方法,运行时寻找匹配的selector然后执行,但是有时候只想在运行时才创建某个方法,消息确没有具体的实现,这个时候就会出出现运行时错误,按照消息转发的顺序我们有三种解决办法. 动态方法处理 首先我们来看一个简单的

ios底层开发消息机制(四)消息转发

消息转发 若想令类能理解某条消息,我们必须以程序码实现出对应的方法才行.但是,在编译期向类发送了其无法解读的消息并不会报错,因为在运行期可以继续向类中添加方法,所以编译器在编译时还无法确知类中到底会不会有某个方法实现.当对象接收到无法解读的消息后,就会启动“消息转发”(message forwarding)机制,程序员可经由此过程告诉对象应该如何处理未知消息. 你可能早就遇到过经由消息转发流程所处理的消息了,只是未加留意.如果在控制台中看到下面这种提示信息,那就说明你曾向某个对象发送过一条其无法

iOS中消息转发的实现

嗯,运行时,运行时是个好东西.在Objective-C语言中,这个特性可以帮助我们干很多的事情. 首先这个特性是把代码的决策从编译和链接时变成运行的时候,这样我们就可以用这个特性来做一些只有在运行的时候才能做到的东西,具体包括: 1.swizzling (交换两个方法的实现) 2.动态方法(可以在运行的时候对一个类进行方法的增加,我们这篇博客会主要讲解这个) 3.相关引用(可以把一个类和一个对象使用一个key进行关联) 4.内省(判断一个对象是否可以调用一个方法) 5.使用emoji字符作为方法

iOS的消息机制和消息转发

1.消息机制 RunTime简称运行时.就是系统在运行的时候的一些机制,其中最主要的是消息机制. 对于C语言,函数的调用在编译的时候会决定调用哪个函数( C语言的函数调用请看这里 ).编译完成之后直接顺序执行,无任何二义性.OC的函数调用成为消息发送.属于动态调用过程.在编译的时候并不能决定真正调用哪个函数(事实证明,在编 译阶段,OC可以调用任何函数,即使这个函数并未实现,只要申明过就不会报错.而C语言在编译阶段就会报错).只有在真正运行的时候才会根据函数的名称找 到对应的函数来调用. [ob