在Objective-C / C中,你能编写一个结合了2个块的函数吗?

我经常发现自己创建了一个“包装器”块,它只用于执行许多其他块,通常具有相同类型的签名。

假设我有2个具有相同类型签名的块:

MyBlockT block1 = ^(NSString *string, id object) { //1 does some work }; MyBlockT block2 = ^(NSString *string, id object) { //2 does some other work }; 

有没有办法实现魔术函数Combine() ,它需要2个块:

 MyBlockT combinedBlock = Combine(block1, block2); //hypothetical function 

并等同于:

 MyBlockT combinedBlock = ^(NSString *string, id object) { block1(string, object); block2(string, object); }; 

我知道这只对返回void块有意义,但这就是我感兴趣的全部内容。

Combinefunction只需要2个块,如果我有更多,我可以链接它们。 我对于如何实现这个问题或者是否可能实现这个问题有所了解。

PS我不介意解决方案是否涉及C宏

编辑

我希望能够将结果块用作方法参数,例如:

 [UIView animateWithDuration:1 animations:someCombinedBlock]; 

现在开始使用GitHub, WoolBlockInvocation !

这是一对类, WSSBlockInvocationWSSBlockSignature ,以及一些支持代码,它们利用libffi和编译器为Blocks生成的ObjC @encode字符串,允许您使用相同的参数集调用整个Blocks列表。

可以将任意数量的块添加到调用对象中,前提是它们的签名(即返回类型,数量和参数类型)匹配。 在调用对象上设置参数后,可以依次调用Blocks,并返回值(如果有),以便以后访问。

您特别感兴趣的部分,将块列表缝合到单个块中,由WSSBlockInvocationinvocationBlock方法提供。

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

这将返回一个块,其中(ab)使用varargsfunction来推迟分配参数,直到实际上自己调用封装块为止。 您可以这样做:

 WSSBlockInvocation * invocation = [WSSBlockInvocation invocationWithBlocks:@[animationBlockOne, animationBlockTwo]]; void (^combinedAnimation)(void) = [invocation invocationBlock]; [UIView animateWithDuration:1 animations:combinedAnimation]; 

当然,如果你只是担心动画的块,没有参数并且没有返回值,那么构造一个包装块是微不足道的:

 void (^combinedAnimation)(void) = ^{ animationBlock(); anotherAnimationBlock(); // etc. }; 

如果需要包装一组Blocks并使用相同的参数集调用它们,则只需要我的代码。

注意我已经在x86_64上的OS X上测试了这个,但是没有在任何其他平台上测试过 。 我希望它适用于iOS下的ARM,但varargs是着名的“不可移植”,它可能不会。 警告compilor,如果有什么事情发生,请告诉我。

这是你想要的?

 MyBlockT CombineBlocks(MyBlockT block1, MyBlockT block2) { return [^(NSString *string, id object) { block1(string, object); block2(string, object); } copy]; } 

该函数创建一个新块,按顺序调用两个给定的块。

这是一个有趣的滥用varargs:

 id combine(id block, ...) { NSMutableArray *blocks = [NSMutableArray array]; //[blocks addObject:block]; va_list objlist; va_start(objlist, block); //while((obj = va_arg(ap, id))) { // } for(id obj = block; obj; obj = va_arg(objlist, id)) { [blocks addObject:[obj copy]]; } va_end(objlist); void (^wrapper)(id,...) = ^(id arg, ...) { NSMutableArray *args = [NSMutableArray array]; va_list arglist; va_start(arglist, arg); for(id x = arg; x; x = va_arg(arglist, id)) { [args addObject:x]; } va_end(arglist); for(void (^blk)() in blocks) { blk(args); } }; return [wrapper copy]; } int main() { NSString *fmt = @"-%d-\n%@\n---"; void (^foo)() = combine(^(NSArray *a){ NSLog(fmt, 1, a); }, ^(NSArray *a){ NSLog(fmt, 2, a); }, nil); foo(@"first", @"second", nil); return 0; } 

您必须定义每个块以接受NSArray参数,并且combine和结果块调用必须至少有一个参数并以nil结尾。

如果您提前知道方法签名,则可以通过适当地更改包装块来解决NSArray并阻止参数限制。

因为你不介意宏

 #define combinedBlock(string, object) \ block1((string), (object) ) \ block2((string), (object) ) 

如果您需要同时执行2个或更多动画,那么RZViewActions就是您需要的一切。 它的代码看起来几乎像animateWithDuration:...调用但具有其他function。

如果你需要同时执行任何块,那么你需要像ReactiveCocoa这样的东西。 但我建议你PromiseKit (因为它更容易)。