问题描述
我经常发现自己创建了一个包装器"块,它只是用于执行许多其他块,通常具有相同的类型签名.
I often find myself creating a "wrapper" block which just serves to execute a number of other blocks, usually with the same type signature.
假设我有 2 个具有相同类型签名的块:
Say I have 2 blocks with the same type signature:
MyBlockT block1 = ^(NSString *string, id object) {
//1 does some work
};
MyBlockT block2 = ^(NSString *string, id object) {
//2 does some other work
};
有没有办法实现魔术函数 Combine()
需要 2 个块:
Is there some way to implement the magic function Combine()
which would take 2 blocks:
MyBlockT combinedBlock = Combine(block1, block2); //hypothetical function
并且相当于做:
MyBlockT combinedBlock = ^(NSString *string, id object) {
block1(string, object);
block2(string, object);
};
我知道这只对返回 void
的块有意义,但这就是我感兴趣的全部.
I know this only makes sense with blocks that return void
, but that's all I'm interested in.
Combine
函数只需要包含 2 个块,如果我有更多,我可以将它们链接起来.我对如何实施这一点或是否有可能感到束手无策.
The Combine
function needs only take in 2 blocks, if I have more I can just chain them. I'm at wits end on how to go about implementing this or whether it's even possible.
附:如果解决方案涉及 C 宏,我不介意
P.S. I wouldn't mind if the solution involved C macros
编辑
我希望能够将生成的块用作方法参数,例如:
I'd like to be able to use the resulting block as a method argument, e.g.:
[UIView animateWithDuration:1 animations:someCombinedBlock];
推荐答案
现在在 GitHub 上,WoolBlockInvocation!
Now up on GitHub, WoolBlockInvocation!
这是一对类,WSSBlockInvocation
和 WSSBlockSignature
,以及一些支持代码,它们利用 libffi 和 ObjC @encode
字符串编译器为 Blocks 生成的它允许您调用具有相同参数集的整个 Block 列表.
This is a pair of classes, WSSBlockInvocation
and WSSBlockSignature
, along with some supporting code, that leverage libffi and the ObjC @encode
strings which the compiler generates for Blocks to allow you to invoke a whole list of Blocks with the same set of arguments.
可以将任意数量的块添加到调用对象中,前提是它们的签名(即返回类型以及参数的数量和类型)匹配.在调用对象上设置参数后,可以依次调用 Blocks,并将返回值(如果有)存储起来以供以后访问.
Any number of Blocks can be added to an invocation object, provided their signatures -- meaning return type and number and types of arguments -- match. After setting arguments on the invocation object, the Blocks can be invoked in turn, with the return values, if any, stored for later access.
invocationBlock
方法的 WSSBlockInvocation
.
- (id)invocationBlock
{
return [^void (void * arg1, ...){
[self setRetainsArguments:YES];
va_list args;
va_start(args, arg1);
void * arg = arg1;
NSUInteger numArguments = [blockSignature numberOfArguments];
for( NSUInteger idx = 1; idx < numArguments; idx++ ){
[self setArgument:&arg atIndex:idx];
arg = va_arg(args, void *);
}
va_end(args);
[self invoke];
} copy];
}
这会返回一个 Block,它 (ab) 使用 varargs 功能来推迟分配参数,直到封装的 Block 本身被实际调用.因此,您可以执行以下操作:
This returns a Block that (ab)uses varargs functionality to defer assigning arguments until that encapsulating Block is actually invoked itself. You can thus do the following:
WSSBlockInvocation * invocation = [WSSBlockInvocation invocationWithBlocks:@[animationBlockOne, animationBlockTwo]];
void (^combinedAnimation)(void) = [invocation invocationBlock];
[UIView animateWithDuration:1 animations:combinedAnimation];
当然,如果你只是担心动画块,不带参数也没有返回值,构造一个包装块是微不足道的:
Of course, if you're just worried about Blocks for animations, that take no arguments and have no return value, constructing a wrapper Block is trivial:
void (^combinedAnimation)(void) = ^{
animationBlock();
anotherAnimationBlock();
// etc.
};
如果你需要包装一组块并使用相同的参数集调用它们,你只需要我的代码.
You only need my code if you need to wrap a set of Blocks and invoke them all with the same set of arguments.
注意我已经在 x86_64 上的 OS X 上测试过这个,但没有在任何其他平台上.我希望它可以在 iOS 下的 ARM 上运行,但是 varargs 以不可移植"而著称,而且可能不是.警告编译器,如果出现问题,请告诉我.
N.B. I have tested this on OS X on x86_64, but not on any other platform. I hope it works on ARM under iOS, but varargs is famously "not portable" and it may not. Caveat compilor, and let me know if something breaks.
这篇关于在 Objective-C/C 中,你能写一个结合 2 个块的函数吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!