通过运行时调用一个块,有类似于NSInvocation的东西吗?

3
我有一个未知类型的块(作为id),还有一个需要传递到该块的参数数组。参数可以是对象或NSNumber/NSValue封装的数字/结构体。块也可能返回对象、数字或结构体。这是库代码,参数类型事先不知道。
假设我可以从块描述符中动态读取签名,是否有一种方法构建类似于NSInvocation的东西来调用块?
2个回答

3

令人惊讶的是,这个方法是可行的:

CGAffineTransform (^block)(id x, int y, CGSize z) = ^(id x, int y, CGSize z){
    NSLog(@"%@,%d,%@", x, y, NSStringFromCGSize(z));
    CGAffineTransform t = { 1, 2, 3, 4, 5, 6 };
    return t;
};

NSMethodSignature* sign = [NSMethodSignature signatureWithObjCTypes:block_signature(block3)];
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:sign];
[invocation setTarget:block3];
void* x = (__bridge void*)@"Foo";
int y = 42;
CGSize z = { 320, 480 };
[invocation setArgument:&x atIndex:1];
[invocation setArgument:&y atIndex:2];
[invocation setArgument:&z atIndex:3];
[invocation invoke];
CGAffineTransform t;
[invocation getReturnValue:&t];

但另一方面,这并非如此:
NSMethodSignature* sign = [self methodSignatureForSelector:@selector(class)];
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:sign];
[invocation setTarget:block];
[invocation setSelector:@selector(class)];
[invocation invoke];
Class k = nil;
[invocation getReturnValue:&k];

根据反汇编,[NSInvocation invoke]的实现会检查目标的类,如果它是一个块(NSBlock的子类),则无论签名如何,它都会始终调用块函数。
更新:已报告为rdar://25289979

1

有的!我在GitHub上创建了一个名为WoolBlockInvocation的项目,旨在镜像NSInvocation的接口以与Blocks一起使用。

实际上,它是作为对In Objective-C/C, can you write a function that combines 2 blocks?的回应而创建的,因此构造函数+ (instancetype)invocationWithBlocks:(NSArray *)blocks;假定您想要将其与多个Blocks一起使用,但它也可以完美地与单个Block一起使用。

它通过WSSBlockSignature助手类为您找出编码签名,然后提供setArgument:atIndex:getReturnValue:invoke等方法,就像NSInvocation一样。
在底层,它使用libffi来完成工作。这需要一定的准备工作来处理结构体: CGSizeCGPointCGRect已经考虑到了,其他可以根据需要添加。注意:我最初是在OS X上的x86_64上测试的,还没有在其他平台上进行过检查。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接