用块包裹块

时间:2013-06-05 03:45:52

标签: objective-c objective-c-blocks

我有一个以块作为参数的方法。该块需要进行扩充,然后传递给作为参数阻塞的库函数。一个例子:

typedef void (^eblock_t)(void);

void libraryFunction(eblock_t block);

- (void)myMethod:(eblock_t)block {
    libraryFunction ( ^{
        block();
        NSLog(@"block executed");  // This is the augmentation of the block
    } );
}

这个例子非常简单,适用于直接的情况。我使用GHUnit将这个例子演变成以下内容。这有点做作,但尽可能简明扼要地说明我的问题:


EBlock.h

typedef void (^eblock_t)(void);

@interface EBlock : NSObject {
    eblock_t _block;
}

@property (nonatomic, readwrite, strong) eblock_t blockOption1;
@property (nonatomic, readwrite, strong) eblock_t blockOption2;

- (void)chooseBlock:(NSUInteger)option;
- (void)executeBlock;

@end

EBlock.m

#import "EBlock.h"

@implementation EBlock

- (void)chooseBlock:(NSUInteger)option {
    if (1 == option) {
        // This is a block wrapping a block to augment the block
        // This is the source of problem with test_switchOption_1For2
        _block = ^{
            self.blockOption1();
            NSLog(@"option1");  // This is the augmentation 
        };
    } else {
        // There is no block wrapping the block and thus no augmentation of the block
        // There is no issue with test_switchOption_2For1
        _block = self.blockOption2;
    }
}

- (void)executeBlock { _block(); }

@end

Test_EBlock.h

@class EBlock;

@interface Test_EBlock : GHTestCase

@property (nonatomic, readonly) NSUInteger counter1;
@property (nonatomic, readonly) NSUInteger counter2;

- (void)incrementCounter1;
- (void)incrementCounter2;

@end

Test_EBlock.m

#import "Test_EBlock.h"
#import "EBlock.h"

@implementation Test_EBlock

- (void)incrementCounter1 { _counter1++; }

- (void)incrementCounter2 { _counter2++; }

- (void)setUp {
    [super setUp];
    _counter1 = _counter2 = 0u;
}

- (void)tearDown { [super tearDown]; }

- (void)test_option1 {
    EBlock *foo = [[EBlock alloc] init];
    foo.blockOption1 = ^{ [self incrementCounter1]; };
    foo.blockOption2 = ^{ [self incrementCounter2]; };
    [foo chooseBlock:1];

    [foo executeBlock];
    GHAssertEquals(self.counter1, 1u, nil);
    GHAssertEquals(self.counter2, 0u, nil);
}

- (void)test_option2 {
    EBlock *foo = [[EBlock alloc] init];
    foo.blockOption1 = ^{ [self incrementCounter1]; };
    foo.blockOption2 = ^{ [self incrementCounter2]; };
    [foo chooseBlock:2];

    [foo executeBlock];
    GHAssertEquals(self.counter1, 0u, nil);
    GHAssertEquals(self.counter2, 1u, nil);
}

- (void)test_switchOption_1For2 {
    EBlock *foo = [[EBlock alloc] init];
    foo.blockOption1 = ^{ [self incrementCounter1]; };
    foo.blockOption2 = ^{ [self incrementCounter2]; };
    [foo chooseBlock:1];

    // switch what is done in the block
    foo.blockOption1 = ^{ [self incrementCounter2]; };

    [foo executeBlock];
    GHAssertEquals(self.counter1, 1u, nil);  // This fails 
    GHAssertEquals(self.counter2, 0u, nil);  // This fails
}

- (void)test_switchOption_2For1 {
    EBlock *foo = [[EBlock alloc] init];
    foo.blockOption1 = ^{ [self incrementCounter1]; };
    foo.blockOption2 = ^{ [self incrementCounter2]; };
    [foo chooseBlock:2];

    // switch what is done in the block
    foo.blockOption2 = ^{ [self incrementCounter1]; };

    [foo executeBlock];
    GHAssertEquals(self.counter1, 0u, nil);
    GHAssertEquals(self.counter2, 1u, nil);
}

讨论

测试:test_option1test_option2,& test_switchOption_2For1通过。

由于test_switchOption_1For2GHAssertEquals(self.counter1, 0u, nil);

GHAssertEquals(self.counter2, 1u, nil);失败

这是因为正在执行的块self.blockOption1实际上是[self incrementCounter2]而不是[self incrementCounter1]。这是因为在EBlock.m chooseBlock中,包装块的块已复制self.blockOption1,其在评估时为[self incrementCounter2]。有没有更好的方法来增加块,所以块不必被包裹?或者有没有办法不延迟对self.blockOption1的评估,使其为[self incrementCounter1]

3 个答案:

答案 0 :(得分:2)

包装块捕获的内容为self,而不是self.blockOption1的值。如果你想捕获后者,请尝试:

- (void)chooseBlock:(NSUInteger)option {
    if (1 == option) {
       eblock_t local_block = self.blockOption1;
        // This is a block wrapping a block to augment the block
        _block = ^{
            local_block();
            NSLog(@"option1");  // This is the augmentation 
        };
    } else {
        // There is no block wrapping the block and thus no augmentation of the block
        // There is no issue with test_switchOption_2For1
        _block = self.blockOption2;
    }
}

答案 1 :(得分:0)

如果我理解正确,您希望将chooseBlock的效果延迟到executeBlock,以便可以在它们之间更改阻止。只需重新安排你的逻辑(直接键入SO,可以整理):

#import "EBlock.h"

@implementation EBlock
{
   NSUInteger currentChoice;
}

- (void)chooseBlock:(NSUInteger)option
{
   currentChoice = option;
}

- (void)executeBlock
{
    if (1 == option)
    {   // This is a block wrapping a block to augment the block
        // This is the source of problem with test_switchOption_1For2
        _block = ^{
            self.blockOption1();
            NSLog(@"option1");  // This is the augmentation 
        };
    }
    else
    {
        // There is no block wrapping the block and thus no augmentation of the block
        // There is no issue with test_switchOption_2For1
        _block = self.blockOption2;
    }

    _block();
}

@end

答案 2 :(得分:-1)

您可能希望复制作为参数/属性进来的块。试试看:

@property (copy) eblock_t block;