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

Eブロック.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]か?

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 に答える