动态调用方法时会用到,例子
-(NSString *)myMethod:(NSString *)param1 withParam2:(NSNumber *)param2
{
NSString *result = @"objc";
NSLog(@"par = %@",param1);
NSLog(@"par 2 = %@",param2);
return result;
}
-(void)invokeMyMethodDynamically
{
SEL selector = @selector(myMethod:withParam2:);
//获得类和方法的签名
NSMethodSignature *methodSignature = [[self class] instanceMethodSignatureForSelector:selector];
//从签名获得调用对象
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
//设置target
[invocation setTarget:self];
//设置selector
[invocation setSelector:selector];
NSString *returnValue = nil;
NSString *argument1 = @"fist";
NSNumber *argument2 = [NSNumber numberWithInt:102];
//设置参数,第一个参数index为2
[invocation setArgument:&argument1 atIndex:2];
[invocation setArgument:&argument2 atIndex:3];
//retain一遍参数
[invocation retainArguments];
//调用
[invocation invoke];
//得到返回值,此时不会再调用,只是返回值
[invocation getReturnValue:&returnValue];
NSLog(@"return value = %@",returnValue);
}
另外一个例子:
SEL selector = @selector(myMethod:setValue2:);
NSMethodSignature *signature = [MyObject instanceMethodSignatureF orSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSign ature:signature];
[invocation setSelector:selector];
NSString *str1 = @"someString";
NSString *str2 = @"someOtherString";
//The invocation object must retain its arguments
[str1 retain];
[str2 retain];
//Set the arguments
[invocation setTarget:targetInstance];
[invocation setArgument:&str1 atIndex:2];
[invocation setArgument:&str2 atIndex:3];
[NSTimer scheduledTimerWithTimeIn terval:0.1 invocation:invocation repeats:YES]
另外
在一般情况下,发送一个无法识别的消息会产生一个运行时的错误,导致应用程序崩溃,但是注意,在崩溃之前,iphone运行时对象为每个对象提供了第二次机会来处理消息。捕捉到一条消息后可以把它重定向到可以响应该消息的对象。
这个功能完全通过消息转发来实现,发送消息给一个无法处理该选择器的对象时,这个选择器就会被转发给 forwardInvocation 方法.接收这条消息的对象,用一个NSInvocation的实例保存原始的选择器和被请求的参数.所以,我们可以覆盖 forwardInvocation 方法,并把消息转发给另外一个对象.
在给程序添加消息转发功能以前,必须覆盖两个方法,即methodSignatureForSelector: 和 forwardInvocation:。methodSignatureForSelector:的作用在于为另一个类实现的消息创建一个有效的方法签名。forwardInvocation:将选择器转发给一个真正实现了该消息的对象.
例子
{
NSMethodSignature* signature = [super methodSignatureForSelector:selector];
if (!signature)
signature = [self.carInfo methodSignatureForSelector:selector];
return signature;
}
SEL selector = [invocation selector];
if ([self.carInfo respondsToSelector:selector]) {
[invocation invokeWithTarget:self.carInfo];
}
}
调用
Car *myCar = [Car car]; // Car为一个类
[(NSString *)myCar UTF8String] // 这里调用NSString中的UTF8String方,注意Car中并未实现该方法
说明
self.carInfo是一个只读的NSString对象,存在于Car类中.例子中Car实例是无法正确的为另外一个对象(NSString)实现的选择器创建一个有效的签名。运行时当检查到当前没有有效的签名,即进入该对象(这里是myCar)的methodSignatureForSelector:方法中,此时,将在这个方法中对每个伪继承进行迭代并尝试构建一个有效的方法签名的机会.例如代码中,当myCar调用UTF8String时,由于无法从当前对象中获得消息,转入第二次机会捕捉消息,首先进入methodSignatureForSelector:方法,采用迭代的方式为当前被调用的方法创建一个有效的签名,得到签名后,转入forwardInvocation:方法对其调用的方法(UTF8String)进行实现. forwardInvocation:中,首先获得调用的方法(UTF8String),判断self.carInfo(一个nsstring对象)能否响应该方法,如果可以,将调用UTF8String对象的目标转换为self.carInfo对象。