使用NSMethodSignature和NSInvocation实现消息转发

参考:

[1]http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSInvocation_Class/Reference/Reference.html

[2]http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100-SW1

[3]http://theocacao.com/document.page/264/

[4]http://blog.sina.com.cn/s/blog_605409770100nib5.html

NSMethodSignature顾名思义应该就是“方法签名”,类似于C++中的编译器对函数进行签名。从这里可以发现,其他很多语言的很多东西都是类似的。官方定义该类为对方法的参数、返回类似进行封装,协同NSInvocation实现消息转发。(Class encapsulating type information for method arguments and return value. It is used as a component of NSInvocation to implement message forwarding。)NSInvocation主要用于不同对象间的消息转发。

首先、什么是消息转发?

我们知道objective-c中调用方法的方式是发消息,那如果给一个实例对象发一个未定义的消息呢?结果就是crash,其实这中间系统给我们第二次机会,就是可以转发该消息。

[cpp]  view plain copy
  1. @interface classA : NSObject {  
  2.   
  3. }  
  4. - (void)classAMethodOne;  
  5. @end  
  6.   
  7. @interface classB : NSObject {  
  8.     classA*   classa_;  
  9. }  
  10.   
  11. @end  

如上述代码所示,如果创建classB类的实例对象instanceB,然后发送[instanceB classAMethodOne]消息,那么就会crash。crash是因为如果objective-c runtime 在运行时动态决议该方法时,resolveClassMethod,不能决议,就是找不到。然后runtime会给该实例第二次机会,首先调用methodSignatureForSelector 或去方法签名,然后调用forwardInvocation,如果用户自己定义的类,没有重写这两个方法,即不支持方法转发,那么就会调用父类NSObject的方法。NSObject父类方法forwardInvocation 中如下所示,所以导致异常,crash。

[cpp]  view plain copy
  1. - (void) forwardInvocation: (NSInvocation*)anInvocation  
  2. {  
  3.   [self doesNotRecognizeSelector:[anInvocation selector]];  
  4.   return;  
  5. }  
  6. - (void) doesNotRecognizeSelector: (SEL)aSelector  
  7. {  
  8.   [NSException raise: NSInvalidArgumentException  
  9.            format: @"%s does not recognize %s",  
  10.            object_get_class_name(self), sel_get_name(aSelector)];  
  11. }  


虽然classB不能响应消息,但classB的成员classa_却能响应该消息。因此,通过将该消息转发给成员classa_即可实现[instanceB classAMethodOne]正常运行。这时,其实也提供了一个Objective-C不支持多重继承的解决方法,通过消息转发,实现classB也能响应ClassA的方法。

其次、如何实现消息转发?

[cpp]  view plain copy
  1. @interface LOCBird : NSObject {  
  2.     NSString* name_;  
  3.       
  4. }  
  5. @end  
  6.   
  7.   
  8. @implementation LOCBird  
  9.   
  10. - (id)init  
  11. {  
  12.     self = [super init];  
  13.     if (self) {  
  14.         name_ = [[NSString alloc] initWithString:@"I am a Bird!!"];  
  15.           
  16.     }  
  17.     return self;  
  18. }  
  19.   
  20. - (void)dealloc  
  21. {  
  22.     [name_  release];  
  23.     [super dealloc];  
  24. }  
  25.   
  26. - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector  
  27. {  
  28.     NSMethodSignature* signature = [super methodSignatureForSelector:aSelector];  
  29.     if (signature==nil) {  
  30.         signature = [name_ methodSignatureForSelector:aSelector];  
  31.     }  
  32.     NSUInteger argCount = [signature numberOfArguments];  
  33.     for (NSInteger i=0 ; i<argCount ; i++) {  
  34.         NSLog(@"%s" , [signature getArgumentTypeAtIndex:i]);  
  35.     }  
  36.     NSLog(@"returnType:%s ,returnLen:%d" , [signature methodReturnType] , [signature methodReturnLength]);  
  37.     NSLog(@"signature:%@" , signature);  
  38.     return signature;  
  39. }  
  40.   
  41. - (void)forwardInvocation:(NSInvocation *)anInvocation  
  42. {  
  43.     NSLog(@"forwardInvocation:%@" , anInvocation);  
  44.     SEL seletor = [anInvocation selector];  
  45.     if ([name_ respondsToSelector:seletor]) {  
  46.         [anInvocation invokeWithTarget:name_];  
  47.     }  
  48.       
  49. }  
  50.   
  51. @end  

如上所示,实现消息转发只需重载NSobject的两个方法,methodSignatureForSelector和forwardInvocation即可实现消息的转发,为了防止转发消息时出错,可以调用respondsToSelector判断能否响应对应的消息。
[cpp]  view plain copy
  1. id bird = [[LOCBird alloc] init];  
  2. NSLog(@"len= %d", [bird length]);  

运行结果如下,其中 @表示对象,:表示a method selector ,具体见参考[2],这两个默认参数就是隐藏的self  和_cmd参数。

2012-03-21 23:40:54.488 LOCMessageForward[1003:207] 0xf263cc

2012-03-21 23:40:54.489 LOCMessageForward[1003:207] @

2012-03-21 23:40:54.491 LOCMessageForward[1003:207] :

2012-03-21 23:40:54.492 LOCMessageForward[1003:207] returnType:I ,returnLen:4

2012-03-21 23:40:54.493 LOCMessageForward[1003:207] signature:<NSMethodSignature: 0x4d255d0>

2012-03-21 23:40:54.493 LOCMessageForward[1003:207] forwardInvocation:<NSInvocation: 0x4d25960>

2012-03-21 23:40:54.495 LOCMessageForward[1003:207] len= 13


一个更简单实现消息转发的方法是只 重载forwardingTargetForSelector方法。

[cpp]  view plain copy
  1. - (id)forwardingTargetForSelector:(SEL)aSelector  
  2. {  
  3.     NSLog(@"forwardingTargetForSelector");  
  4.     if ([name_ respondsToSelector:aSelector]) {  
  5.         return name_;  
  6.     }  
  7.     return nil;  
  8. }  




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值