这是对于 Cocoa/Cocoa Touch 框架的查漏补缺行动,也是对平时不常用的、零碎的知识的总结。不积跬步,无以至千里嘛。这次的主角是 NSInvocation
。
NSInvocation
NSInvocation 的文档 说明这个类的作用。它的实例封装了一个动作,包含了 Objective-C 中消息的所有元素:target,selector,arguments 和 return value。
NSInvocation
只有 +invocationWithMethodSignature:
一个类方法获得实例,需要 NSMethodSignature
实例作为参数,之后还需设置它的 selector
属性。因为除了通过 NSMethodSignature
实例得到参数类型和返回值类型的信息之外,还需知道方法的名字,不然无法找到方法实现的地址,更不可能调用方法实现那段子程序。
下面的代码创建了一个 NSInvocation 对象并执行:
@interface TestClass : NSObject
-(NSString *)greet:(NSString *)str;
@end
@implementation TestClass
-(NSString *)greet:(NSString *)str {
NSString *greetStr = [NSString stringWithFormat:@"Hello, %@!", str];
return greetStr;
}
@end
int main (int argc, char *argv[]) {
TestClass *tester = [[TestClass alloc] init];
NSString *string = @"Stan";
NSString * returnString;
NSMethodSignature *signture = [TestClass instanceMethodSignatureForSelector:@selector(greet:)];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signture];
[invocation setTarget:tester];
[invocation setSelector:@selector(greet:)];
[invocation setArgument:&string atIndex:2];
[invocation invoke];
[invocation getReturnValue:&returnString];
NSLog(@"%@", returnString);
return 0;
}
注意点
-
-setArgument:
的 index 一定是从2开始的,0和1已经被作为为隐式传递的参数self
和_cmd
占据; -
-setArgument:
不会 retain 传入的参数,这意味着在 invoke 之前要保证作为参数的对象不被释放,你可以通过-retainArguments
去 retain 参数; - 在
NSTimer
的+scheduledTimerWithTimeInterval:invocation:repeats:
方法中会通过-retainArguments
去 retain 参数,添加一个 symbolic breakpoint 可以证明这一点; - 如果返回值不是 Objective-C 中的对象,需要从方法签名中得到返回值占用空间的大小(
methodReturnLength
属性),并通过void *buffer = malloc(size)
分配空间,把返回值的内容拷贝到 buffer 指向的空间去; -
-getReturnValue:
当然要在-invoke
之后执行,否则结果是未定义的。