参考:
[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;
}