Calling a block though runtime, anything similar to NSInvocation?

空扰寡人 提交于 2019-12-24 01:45:24

问题


I have block of unknown type (as id) and array of arguments that need to passed into that block. Arguments may be objects or numbers/structs boxed as NSNumber/NSValue. Block may also return an object, number or struct. This is a library code, and types of arguments are not known beforehand.

Assuming that I can can dynamically read signature from the block descriptor, is there a way to construct something like an NSInvocation to call a block?


回答1:


Surprisingly this works:

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];

But on the other hand, this does not:

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];

AFAICS from the disassembly, implementation of the [NSInvocation invoke] checks for the class of the target, and if it is a block (subclass of NSBlock) then it always calls block function, regardless of the signature.

Updated: Reported as rdar://25289979




回答2:


There is! I have a project on GitHub called WoolBlockInvocation which aims to mirror NSInvocation's interface for use with Blocks.

It was actually created in response to In Objective-C/C, can you write a function that combines 2 blocks?, so the constructor + (instancetype)invocationWithBlocks:(NSArray *)blocks; assumes that you want to use it with multiple Blocks, but it works perfectly well with a single Block.

It figures out the encoding signature for you via the WSSBlockSignature helper class,* and gives you setArgument:atIndex:, getReturnValue:, invoke, and other methods just like NSInvocation's.

Under the hood, it uses libffi to do the work. This requires a certain amount of prep to handle structs: CGSize, CGPoint, and CGRect are accounted for, and others can be added as needed.

(Cave: I originally tested it on OS X, x86_64. It hasn't been checked out on any other platform.)



来源:https://stackoverflow.com/questions/36153926/calling-a-block-though-runtime-anything-similar-to-nsinvocation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!