簡體   English   中英

在Objective-C / C中,您可以編寫包含2個塊的函數嗎?

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

這是一對WSSBlockInvocationWSSBlockSignature類,以及一些支持代碼,它們利用libffi和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)使用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM