Cocoa 拾遗 —— NSInvocation

这是对于 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;
}

注意点

  1. -setArgument: 的 index 一定是从2开始的,0和1已经被作为为隐式传递的参数 self_cmd 占据;
  2. -setArgument: 不会 retain 传入的参数,这意味着在 invoke 之前要保证作为参数的对象不被释放,你可以通过 -retainArguments 去 retain 参数;
  3. NSTimer+scheduledTimerWithTimeInterval:invocation:repeats: 方法中会通过 -retainArguments 去 retain 参数,添加一个 symbolic breakpoint 可以证明这一点;
  4. 如果返回值不是 Objective-C 中的对象,需要从方法签名中得到返回值占用空间的大小(methodReturnLength 属性),并通过 void *buffer = malloc(size) 分配空间,把返回值的内容拷贝到 buffer 指向的空间去;
  5. -getReturnValue: 当然要在 -invoke 之后执行,否则结果是未定义的。

你可能感兴趣的:(Cocoa 拾遗 —— NSInvocation)