0

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

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_option1, test_option2, &test_switchOption_2For1通过。

test_switchOption_1For2失败是因为GHAssertEquals(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]

4

3 回答 3

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;
    }
}
于 2013-06-05T21:19:15.293 回答
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
于 2013-06-05T04:15:36.737 回答
-1

您可能想要复制作为参数/属性传入的块。尝试使用:

@property (copy) eblock_t block;
于 2013-06-05T20:35:37.987 回答