参考:
[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,其实这中间系统给我们第二次机会,就是可以转发该消息。
- @interface classA : NSObject {
- }
- - (void)classAMethodOne;
- @end
- @interface classB : NSObject {
- classA* classa_;
- }
- @end
如上述代码所示,如果创建classB类的实例对象instanceB,然后发送[instanceB classAMethodOne]消息,那么就会crash。crash是因为如果objective-c runtime 在运行时动态决议该方法时,resolveClassMethod,不能决议,就是找不到。然后runtime会给该实例第二次机会,首先调用methodSignatureForSelector 或去方法签名,然后调用forwardInvocation,如果用户自己定义的类,没有重写这两个方法,即不支持方法转发,那么就会调用父类NSObject的方法。NSObject父类方法forwardInvocation 中如下所示,所以导致异常,crash。
- - (void) forwardInvocation: (NSInvocation*)anInvocation
- {
- [self doesNotRecognizeSelector:[anInvocation selector]];
- return;
- }
- - (void) doesNotRecognizeSelector: (SEL)aSelector
- {
- [NSException raise: NSInvalidArgumentException
- format: @"%s does not recognize %s",
- object_get_class_name(self), sel_get_name(aSelector)];
- }
虽然classB不能响应消息,但classB的成员classa_却能响应该消息。因此,通过将该消息转发给成员classa_即可实现[instanceB classAMethodOne]正常运行。这时,其实也提供了一个Objective-C不支持多重继承的解决方法,通过消息转发,实现classB也能响应ClassA的方法。
其次、如何实现消息转发?
- @interface LOCBird : NSObject {
- NSString* name_;
- }
- @end
- @implementation LOCBird
- - (id)init
- {
- self = [super init];
- if (self) {
- name_ = [[NSString alloc] initWithString:@"I am a Bird!!"];
- }
- return self;
- }
- - (void)dealloc
- {
- [name_ release];
- [super dealloc];
- }
- - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
- {
- NSMethodSignature* signature = [super methodSignatureForSelector:aSelector];
- if (signature==nil) {
- signature = [name_ methodSignatureForSelector:aSelector];
- }
- NSUInteger argCount = [signature numberOfArguments];
- for (NSInteger i=0 ; i<argCount ; i++) {
- NSLog(@"%s" , [signature getArgumentTypeAtIndex:i]);
- }
- NSLog(@"returnType:%s ,returnLen:%d" , [signature methodReturnType] , [signature methodReturnLength]);
- NSLog(@"signature:%@" , signature);
- return signature;
- }
- - (void)forwardInvocation:(NSInvocation *)anInvocation
- {
- NSLog(@"forwardInvocation:%@" , anInvocation);
- SEL seletor = [anInvocation selector];
- if ([name_ respondsToSelector:seletor]) {
- [anInvocation invokeWithTarget:name_];
- }
- }
- @end
如上所示,实现消息转发只需重载NSobject的两个方法,methodSignatureForSelector和forwardInvocation即可实现消息的转发,为了防止转发消息时出错,可以调用respondsToSelector判断能否响应对应的消息。
- id bird = [[LOCBird alloc] init];
- 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方法。
- - (id)forwardingTargetForSelector:(SEL)aSelector
- {
- NSLog(@"forwardingTargetForSelector");
- if ([name_ respondsToSelector:aSelector]) {
- return name_;
- }
- return nil;
- }