在我的应用中,我将NSTimer
与传递给方法的块相关联;该块也被添加到一个块数组中。当计时器触发时,将调用其关联的块,并应从阵列中删除。所以我的设置如下:
@interface MyObject : NSObject
@property(strong, nonatomic) NSMutableArray *allBlocks;
- (void)myMethodWithBlock:(void(^)(void))block;
- (void)timerFired:(NSTimer *)timer;
@end
@implementation MyObject
- (id)init
{
self = [super init];
if (self)
{
self.allBlocks = [NSMutableArray array];
}
return self;
}
- (void)myMethodWithBlock:(void(^)(void))block
{
[NSTimer scheduledTimerWithTimeInterval:5.0f
target:self
selector:@selector(timerFired:)
userInfo:block
repeats:NO];
[self.allBlocks addObject:block];
}
- (void)timerFired:(NSTimer *)timer
{
void(^block)(void) = timer.userInfo;
[self.allBlocks removeObject:block];
block();
}
@end
我的问题是,当调用timerFired:
时,(有时)不会删除块。为什么呢?
答案 0 :(得分:3)
这里的问题是NSTimer
复制了分配给userInfo
的块,但传递给myMethodWithBlock:
的块可能是NSStackBlock
的一个实例,它是不等于其副本。
让我们考虑三种情况,其中myObject
是MyObject
的一个实例:
// A
void(^myBlock)(void) = ^{
NSLog(@"1");
};
[myObject myMethodWithBlock:myBlock];
// B
int one = 1;
void(^myBlock)(void) = ^{
NSLog(@"%d", one);
};
[myObject myMethodWithBlock:myBlock];
// C
int one = 1;
[myObject myMethodWithBlock:^{
NSLog(@"%d", one);
};];
NSGlobalBlock
的一个实例,它在复制时只返回自身。one
;它将是NSMallocBlock
的一个实例,它在复制时也会返回。one
,但在传递给myMethodWithBlock:
之前也未分配给变量。在这种情况下,该块是NSStackBlock
的实例,在复制时返回NSMallocBlock
的实例。这样做的结果是,在情景C的情况下,NSStackBlock
将添加到allBlocks
,而NSMallocBlock
将被分配给计时器userInfo
{1}}。当计时器触发时,removeObject:
不执行任何操作,因为分配给计时器的块不等于阵列中的任何块。
解决方案是在将块存储在数组中之前始终复制块。这样,相同的块实例将存储在数组中并分配给计时器:
- (void)myMethodWithBlock:(void(^)(void))block
{
block = [block copy];
[NSTimer scheduledTimerWithTimeInterval:5.0f
target:self
selector:@selector(timerFired:)
userInfo:block
repeats:NO];
[self.allBlocks addObject:block];
}
答案 1 :(得分:1)
更明确的方法是使用isEqual:
行为众所周知且更具可读性的内容标记块,如NSNumber
...
// keep state so these can be made unique
@property(nonatomic, assign) NSInteger blockIndex;
// change the blocks collection to record blocks' associations with numbers
@property(nonatomic, strong) NSMutableDictionary *allBlocks;
// in myMethod...
NSNumber *nextIndex = [NSNumber numberWithInt:++self.blockIndex];
self.allBlocks[nextIndex] = block;
// pass userInfo:nextIndex when you schedule the timer
现在,计时器上下文永远不会有块,复制或其他方式。然后,当计时器触发......
- (void)timerFired:(NSTimer *)timer {
NSNumber *index = timer.userInfo;
void(^block)(void) = self.allBlocks[index];
[self.allBlocks removeObjectForKey:index];
block();
}
答案 2 :(得分:0)
我认为将苹果与苹果进行比较是最安全的。
- (void)myMethodWithBlock:(void(^)(void))block
{
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:5.0f
target:self
selector:@selector(timerFired:)
userInfo:block
repeats:NO];
[self.allBlocks addObject:timer.userInfo];
}
- (void)timerFired:(NSTimer *)timer
{
[self.allBlocks removeObject:timer.userInfo];
void(^block)(void) = timer.userInfo;
block();
}