[英]In Objective-C/C, can you write a function that combines 2 blocks?
我经常发现自己创建了一个“包装器”块,它只用于执行许多其他块,通常具有相同类型的签名。
假设我有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
功能只需要2个块,如果我有更多,我可以链接它们。 我知道如何实现这个或者是否可能。
PS我不介意解决方案是否涉及C宏
编辑
我希望能够将结果块用作方法参数,例如:
[UIView animateWithDuration:1 animations:someCombinedBlock];
这是你想要的?
MyBlockT CombineBlocks(MyBlockT block1, MyBlockT block2)
{
return [^(NSString *string, id object) {
block1(string, object);
block2(string, object);
} copy];
}
该函数创建一个新块,按顺序调用两个给定的块。
现在在GitHub上, WoolBlockInvocation !
这是一对WSSBlockInvocation
和WSSBlockSignature
类,以及一些支持代码,它们利用libffi和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)使用varargs功能来推迟分配参数,直到实际上自己调用封装块为止。 您可以这样做:
WSSBlockInvocation * invocation = [WSSBlockInvocation invocationWithBlocks:@[animationBlockOne, animationBlockTwo]];
void (^combinedAnimation)(void) = [invocation invocationBlock];
[UIView animateWithDuration:1 animations:combinedAnimation];
当然,如果您只是担心动画块,该块不带任何参数且没有返回值,则构造包装器Block是很简单的:
void (^combinedAnimation)(void) = ^{
animationBlock();
anotherAnimationBlock();
// etc.
};
仅当需要包装一组Block并使用相同的一组参数调用它们时,才需要我的代码。
注意:我已经在x86_64的OS X上对此进行了测试,但是没有在任何其他平台上进行过测试。 我希望它可以在iOS下的ARM上运行,但是varargs众所周知是“不可移植的”,而且可能不是。 请注意编译器,如果有问题请通知我。
这是一个有趣的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:...
调用但具有其他功能。
如果你需要同时执行任何块,那么你需要像ReactiveCocoa这样的东西。 但我建议你PromiseKit (因为它更容易)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.