iOS方法调用之NSInvocation

在iOS中方法调用有两种方法,一种是performSelecter系列方法,也是我们最常见的使用方法。
例如,调用测试类中的方法:

1
2
3
4
5
6
7
8
@implementation TestClass
- (NSString *)testWithArg1:(NSString *)arg1 arg2:(NSString *)arg2
{
NSString *res = [NSString stringWithFormat:@"you called me with %@ and %@", arg1, arg2];
NSLog(@"%@", res);
return res;
}
@end

我们可以这么做:

1
2
3
NSString *res = [[[TestClass alloc] init] performSelector:@selector(testWithArg1:arg2:)
withObject:@"t1" withObject:@"t2"];
NSLog(@"%@", res);

然而,这种方法也是有缺点的,比如参数的个数一定,如果参数过多的话,就无法使用了。所以,我们还能通过NSInvocation来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
TestClass *tcls = [[TestClass alloc] init];
SEL selector = NSSelectorFromString(@"testWithArg1:arg2:");
NSMethodSignature *signature = [[TestClass class] instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.selector = selector;
NSArray *argsArray = @[@"t1", @"t2"];
// 传递参数的个数根据方法的参数个数来定,参数智能少不能多,当然了如果参数少了肯定在方法调用的时候肯定会有问题
for (NSInteger i = 0; i < argsArray.count; i ++) {
id arg = argsArray[i];
[invocation setArgument:&arg atIndex:2+ i];
}
[invocation retainArguments];
// target也有一个property属性来设置,如果设置了调用invoke方法就行了
[invocation invokeWithTarget:tcls];
// 修改返回值,当然,正常的时候是不需要的
NSString *res = @"haha";
[invocation setReturnValue:&res];
id ret;
// 获取调用方法后返回值,需要注意的是,如果调用的方法是没有返回值的话,在这里调用的时候会造成程序的崩溃。
[invocation getReturnValue:&ret];
NSLog(@"%@", ret);

为什么设置参数的时候是从2开始呢?通过getArgument:atIndex:方法可以知道第一个参数是target,第二个参数是selector