在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
块有意义,但这就是我感兴趣的全部内容。
Combine
function只需要2个块,如果我有更多,我可以链接它们。 我对于如何实现这个问题或者是否可能实现这个问题有所了解。
PS我不介意解决方案是否涉及C宏
编辑
我希望能够将结果块用作方法参数,例如:
[UIView animateWithDuration:1 animations:someCombinedBlock];
现在开始使用GitHub, WoolBlockInvocation !
这是一对类, WSSBlockInvocation
和WSSBlockSignature
,以及一些支持代码,它们利用libffi和编译器为Blocks生成的ObjC @encode
字符串,允许您使用相同的参数集调用整个Blocks列表。
可以将任意数量的块添加到调用对象中,前提是它们的签名(即返回类型,数量和参数类型)匹配。 在调用对象上设置参数后,可以依次调用Blocks,并返回值(如果有),以便以后访问。
您特别感兴趣的部分,将块列表缝合到单个块中,由WSSBlockInvocation
的invocationBlock
方法提供。
- (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 (因为它更容易)。