您的位置:首页 > 移动开发 > Objective-C

消息转发原理浅析

2016-01-08 16:20 381 查看
消息:在iOS中调用方法就是向对象发送消息。

对于问题是如果没有这个方法调用,那么消息不就是发不了了?最经典的报错就是说不能识别一个实例的方法。

Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[ViewController temBtnClick:]: unrecognized selector sent to instance 0x7b98c9a0’

分析:

方法在调用时,系统会查看这个对象能否接收到这个消息(前提是否有)。如果不能接收系统会默认采取一些方法(你只需要实现),给你补救的机会。 接下来的事情就是在这个补救过程中开展了。

方案一:动态方法解析(Dynamic Method Resolution或Lazy method resolution)

向当前类(
Class
)发送
resolveInstanceMethod:
(对于类方法则为
resolveClassMethod:
)消息,如果返回
YES
,则系统认为请求的方法已经加入到了,则会重新发送消息。

+ (BOOL)resolveInstanceMethod:(SEL)sel // 对象(实例)
+ (BOOL)resolveClassMethod:(SEL)sel  // 类


方案二:快速转发路径(Fast forwarding path)

若果当前
target
实现了
forwardingTargetForSelector:
方法,则调用此方法。如果此方法返回除nil和self的其他对象,则向返回对象重新发送消息

- (id)forwardingTargetForSelector:(SEL)aSelector // 返回一个有所需要方法的对象


方案三:慢速转发路径(Normal forwarding path)

首先
runtime
发送
methodSignatureForSelector:
消息查看
Selector
对应的方法签名,即参数与返回值的类型信息例:
(v@:@)


其次如果有方法签名返回,
runtime
则根据方法签名创建描述该消息的
NSInvocation
,向当前对象发送
forwardInvocation:
消息,以创建的
NSInvocation
对象作为参数;

methodSignatureForSelector:
无方法签名返回,则向当前对象发送
doesNotRecognizeSelector:
消息,程序抛出异常退出。

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector; // 返回签名
- (void)forwardInvocation:(NSInvocation *)anInvocation; // 执行


示例如下:

创建一个控制器创建一个button,并添加方法

viewDidLoad:

UIButton *temBtn = [UIButton buttonWithType:UIButtonTypeCustom];
temBtn.frame = CGRectMake(40, 100, 80, 40);
[temBtn setTitle:@"测试" forState:UIControlStateNormal];
[temBtn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[temBtn addTarget:self action:@selector(temBtnClick:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:temBtn];


#warning 假设没有实现
/*
- (void)temBtnClick:(UIButton *)btn {
NSLog(@"现在执行到这个方法");
}
*/

#warning 方案一
/**
描述:
向当前类(class)发送resolveInstanceMethod:(类方法则为resolveClassMethod:)消息,如果返回YES,则系统认为请求的方法已经加入到了,则会重新发送消息。
*/

/*
void click(id self,SEL _cmd,UIButton *btn) {
NSLog(@"成功的解决了问题,%@",btn);
}

+ (BOOL)resolveInstanceMethod:(SEL)sel { // 只能掌管一个类中的方法

if (sel == @selector(temBtnClick:)) {
class_addMethod(self, sel, (IMP)click, "v@:@");
return YES;
}

return [super resolveInstanceMethod:sel];
}

*/

#warning 方案二 (快速转发路径 Fast forwarding path)
/*
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSLog(@"走到了这里");
// 需要创建一个Deal类,并实现temBtnClick:这个方法
return [[Deal alloc] init];  // 此实例拥有temBtnClick:这个方法可以处理
}
*/

#warning 方案三 (慢速转发路径  Normal forwarding path)

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
// 为方法生成方法签名,这个签名就是给下面的anInvocation调用的   invocation中有一个方法就是通过sig生成的
NSString *sel = NSStringFromSelector(aSelector);
if ([sel isEqualToString:@"temBtnClick:"]) {
NSMethodSignature *sig = [NSMethodSignature signatureWithObjCTypes:"v@:@"]; // 方法签名
return sig;
}
return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL selector = [anInvocation selector];
Deal *deal = [[Deal alloc] init];
if ([deal respondsToSelector:selector]) {
[anInvocation invokeWithTarget:deal];
}
}


参考:

http://blog.csdn.net/c395565746c/article/details/8507008 (比较全面深入)

http://www.cocoachina.com/ios/20150604/12013.html (比较易懂)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ios 消息转发 Objective