ブロックを引数として取るメソッドがあります。そのブロックを拡張してから、ブロックするライブラリ関数に引数として渡す必要があります。例:
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_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]
か?