我有一个以块作为参数的方法。该块需要进行扩充,然后传递给作为参数阻塞的库函数。一个例子:
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将这个例子演变成以下内容。这有点做作,但尽可能简明扼要地说明我的问题:
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
#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
@class EBlock;
@interface Test_EBlock : GHTestCase
@property (nonatomic, readonly) NSUInteger counter1;
@property (nonatomic, readonly) NSUInteger counter2;
- (void)incrementCounter1;
- (void)incrementCounter2;
@end
#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]
。
答案 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;