繁体   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