消息转发
消息转发分为俩大阶段
- 动态方法解析
- 完整的消息转发机制
- 消息转发全流程:
- 若对象无法响应某个选择器,则进入消息转发流程
2.通过运行期间的动态方法解析,可以再需要用到某个方法时再将其加入类中 3.对象可以把其无法解读的某些选择器转交给其他对象处理 4.经过上述两步,如果还是不能处理选择器,那就启动完整的消息转发机制
消息转发全流程.png
动态方法解析(动态添加方法)
当一个实例对象调用一个不存在的方法,为了程序不carsh,我们可以动态的为其添加方法
代码语言:javascript复制//Phone并没有say方法的实现
Phone *phone = [[Phone alloc]init];
[phone say];
代码语言:javascript复制//程序carsh报告
-[Phone say]: unrecognized selector sent to instance 0x600000005b90
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Phone say]: unrecognized selector sent to instance 0x600000005b90'
- 第一步:对象在收到无法解读的消息后,首先会调用 (BOOL)resolveInstanceMethod:(SEL)sel 或者 (BOOL)resolveClassMethod:(SEL)sel
#import "Phone.h"
#import <objc/runtime.h>
@implementation Phone
void sayHello(){
NSLog(@"sayHello");
}
(BOOL)resolveInstanceMethod:(SEL)sel{
//判断方法名是否是sayHello,有则加
if ([NSStringFromSelector(sel) hasSuffix:@"sayHello"]) {
// 第一个参数:给哪个类添加方法
// 第二个参数:方法名
// 第三个参数:添加方法的函数实现(函数地址)
// 第四个参数:函数的类型,(返回值 参数类型) v:void @:对象->self :表示SEL->_cmd
class_addMethod([self class], sel, (IMP)sayHello, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
完整的消息转发机制
- 第二步:第一步执行完,如果没有新增方法,运行期系统会把这个消息转给其他接收者处理,系统会调用这个方法
- (id)forwardingTargetForSelector:(SEL)aSelector
此时,我们需要找一个与Phone相关连的类去处理这个消息,如果这个相关联的对象可以处理这个消息,则返回这个对象,若不能则返回nil。
代码语言:javascript复制#import "Phone.h"
#import <objc/runtime.h>
#import "iPhone.h"
@implementation Phone
//第一步
(BOOL)resolveInstanceMethod:(SEL)sel{
return [super resolveInstanceMethod:sel];
}
//第二步
- (id)forwardingTargetForSelector:(SEL)aSelector{
iPhone *phone = [[iPhone alloc]init];
if ([phone respondsToSelector:aSelector]) {
return phone;
}
return [super forwardingTargetForSelector: aSelector];
}
- 第三步:如果第二步返回nil,那就会走到最后一步,这步也是代价最大的一步
#import "Phone.h"
#import <objc/runtime.h>
#import "iPhone.h"
@implementation Phone
//第一步
(BOOL)resolveInstanceMethod:(SEL)sel{
return [super resolveInstanceMethod:sel];
}
//第二步
- (id)forwardingTargetForSelector:(SEL)aSelector{
return [super forwardingTargetForSelector: aSelector];
}
//第三步
//首先寻找方法签名,如果没有,则会调用系统根类的doesNotRecognizeSelector:方法,不会进入到下面的消息分发.
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
if(aSelector == @selector(sayHello)){
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
//如果上面的方法签名找到了,则会调用这个方法.将消息传递给其他对象,可以传递给多个对象,通过anInvocation拿到相应信息
-(void)forwardInvocation:(NSInvocation *)anInvocation{
if (anInvocation.selector == @selector(sayHello)){
iPhone *phone = [[iPhone alloc]init];
[anInvocation invokeWithTarget:phone];
}
}
//寻找方法签名,如果没有找到,则回调这个方法
- (void)doesNotRecognizeSelector:(SEL)aSelector{
NSLog(@"消息无法响应");
}
参考
Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法