使用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,其实这中间系统给我们第二次机会,就是可以转发该消息。

@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;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值