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函数只需要占用 2 个块,如果我有更多块,我可以将它们链接起来。我对如何实施这一点或它是否可能无计可施。

PS我不介意解决方案是否涉及C宏

编辑

我希望能够将生成的块用作方法参数,例如:

[UIView animateWithDuration:1 animations:someCombinedBlock];
4

5 回答 5

4

这是你想要的?

MyBlockT CombineBlocks(MyBlockT block1, MyBlockT block2)
{
    return [^(NSString *string, id object) {
        block1(string, object);
        block2(string, object);
    } copy];
}

该函数创建一个新的块,依次调用两个给定的块。

于 2013-07-14T10:58:11.100 回答
2

现在在 GitHub 上,WoolBlockInvocation

这是一对类,WSSBlockInvocation以及WSSBlockSignature一些支持代码,它们利用 libffi 和@encode编译器为 Blocks 生成的 ObjC 字符串,允许您调用具有相同参数集的整个 Block 列表。

可以将任意数量的块添加到调用对象中,前提是它们的签名——意味着返回类型以及参数的数量和类型——匹配。在调用对象上设置参数后,可以依次调用块,并存储返回值(如果有)以供以后访问。

您特别感兴趣的部分,将块列表缝合到一个块中,invocationBlockWSSBlockInvocation.

- (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];
}

这将返回一个 Block,它 (ab) 使用 varargs 功能来推迟分配参数,直到封装的 Block 本身被实际调用。因此,您可以执行以下操作:

WSSBlockInvocation * invocation = [WSSBlockInvocation invocationWithBlocks:@[animationBlockOne, animationBlockTwo]];

void (^combinedAnimation)(void) = [invocation invocationBlock];

[UIView animateWithDuration:1 animations:combinedAnimation];

当然,如果你只是担心动画块,它不带参数也没有返回值,构造一个包装块是微不足道的:

void (^combinedAnimation)(void) = ^{
    animationBlock();
    anotherAnimationBlock();
    // etc.
};

如果您需要包装一组块并使用相同的参数集调用它们,您只需要我的代码。

注意我已经在 x86_64 上的 OS X 上对此进行了测试,但没有在任何其他平台上进行测试。我希望它可以在 iOS 下的 ARM 上运行,但是 varargs 以“不可移植”而著称,而且可能不是。警告编译器,如果有问题请告诉我。

于 2013-07-26T22:49:57.123 回答
1

这是对可变参数的有趣滥用:

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 和块参数限制。

于 2013-07-14T19:34:32.813 回答
0

因为你不介意宏

#define combinedBlock(string, object)      \
         block1((string), (object) )       \            
         block2((string), (object) )
于 2013-07-14T10:50:12.363 回答
0

如果您需要同时执行 2 个或更多动画,那么RZViewActions就是您所需要的一切。它的代码看起来几乎像animateWithDuration:...调用,但具有附加功能。

如果您需要同时执行任何块,那么您需要类似ReactiveCocoa的东西。但我建议你使用 PromiseKit(只是因为它更容易)。

于 2015-08-03T07:42:08.963 回答