[英]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.