GCD 计时器经常崩溃
GCD Timer crashes with frequently
我用 GCD dispatch_source 创建了一个非常简单的计时器,我经常收到这种奇怪的崩溃。代码对我来说看起来很好。如果有人能解释这背后的原因,我将非常高兴。
@interface MyTimer ()
@property (nonatomic, strong, readonly) dispatch_queue_t queue;
@property (nonatomic, readonly) NSTimeInterval timeOutDuration;
@property (nonatomic, copy, readonly) dispatch_block_t callback;
@property (nonatomic, strong) dispatch_group_t group;
@property (nonatomic, strong) dispatch_source_t timer;
@end
@implementation MyTimer
- (instancetype)initWithQueue:(dispatch_queue_t)queue
callback:(dispatch_block_t)callback
timeOutDuration:(NSTimeInterval)interval
{
NSAssert(queue, @"queue must not be nil");
NSAssert(callback, @"callback must not be nil");
if (self = [super init]) {
_queue = queue;
_callback = [callback copy];
_timeOutDuration = interval;
_group = dispatch_group_create();
[self setupTimer];
}
return self;
}
- (void)setupTimer
{
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.queue);
dispatch_source_set_timer(self.timer, dispatch_time(DISPATCH_TIME_NOW, self.timeOutDuration * NSEC_PER_SEC), 0, 0);
__weak typeof(self) weakSelf = self;
dispatch_source_set_event_handler(self.timer, ^{
__strong typeof(self) strongSelf = weakSelf;
// crashes on the next line
dispatch_group_async(strongSelf.group, dispatch_get_main_queue(), strongSelf.callback);
dispatch_group_async(strongSelf.group, strongSelf.queue, ^{
[strongSelf invalidate];
});
});
}
- (void)fire
{
dispatch_resume(self.timer);
}
- (void)invalidate
{
if (self.timer) {
dispatch_source_cancel(self.timer);
_timer = nil;
_callback = nil;
}
}
@end
它通常在这一行崩溃,
dispatch_group_async(strongSelf.group, dispatch_get_main_queue(), strongSelf.callback);
崩溃并非总是发生,而是经常发生。我无法理解崩溃背后的逻辑。
假设计时器将块排队等待执行。当块挂起时,MyTimer
对象的保留计数变为零,并且 MyTimer
被释放。然后弱引用weakSelf
设置为nil.
然后块执行。它从 weakSelf
创建 strongSelf
。由于 weakSelf
现在为零,因此 strongSelf
也为零。所以 strongSelf.group
和 strongSelf.callback
都是 return nil,你将这些 nils 传递给 dispatch_group_async
。糟糕的节目!没有饼干!
要修复,请在创建后立即检查 strongSelf
。如果为零,则 return.
我还注意到您在 MyTimer
中没有 dealloc
方法。我怀疑恢复的 GCD 定时器被保留,所以当你的 MyTimer
被释放时它不会是 invalidated/released。您可能应该添加一个 dealloc
来执行 [self invalidate]
.
我用 GCD dispatch_source 创建了一个非常简单的计时器,我经常收到这种奇怪的崩溃。代码对我来说看起来很好。如果有人能解释这背后的原因,我将非常高兴。
@interface MyTimer ()
@property (nonatomic, strong, readonly) dispatch_queue_t queue;
@property (nonatomic, readonly) NSTimeInterval timeOutDuration;
@property (nonatomic, copy, readonly) dispatch_block_t callback;
@property (nonatomic, strong) dispatch_group_t group;
@property (nonatomic, strong) dispatch_source_t timer;
@end
@implementation MyTimer
- (instancetype)initWithQueue:(dispatch_queue_t)queue
callback:(dispatch_block_t)callback
timeOutDuration:(NSTimeInterval)interval
{
NSAssert(queue, @"queue must not be nil");
NSAssert(callback, @"callback must not be nil");
if (self = [super init]) {
_queue = queue;
_callback = [callback copy];
_timeOutDuration = interval;
_group = dispatch_group_create();
[self setupTimer];
}
return self;
}
- (void)setupTimer
{
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.queue);
dispatch_source_set_timer(self.timer, dispatch_time(DISPATCH_TIME_NOW, self.timeOutDuration * NSEC_PER_SEC), 0, 0);
__weak typeof(self) weakSelf = self;
dispatch_source_set_event_handler(self.timer, ^{
__strong typeof(self) strongSelf = weakSelf;
// crashes on the next line
dispatch_group_async(strongSelf.group, dispatch_get_main_queue(), strongSelf.callback);
dispatch_group_async(strongSelf.group, strongSelf.queue, ^{
[strongSelf invalidate];
});
});
}
- (void)fire
{
dispatch_resume(self.timer);
}
- (void)invalidate
{
if (self.timer) {
dispatch_source_cancel(self.timer);
_timer = nil;
_callback = nil;
}
}
@end
它通常在这一行崩溃,
dispatch_group_async(strongSelf.group, dispatch_get_main_queue(), strongSelf.callback);
崩溃并非总是发生,而是经常发生。我无法理解崩溃背后的逻辑。
假设计时器将块排队等待执行。当块挂起时,MyTimer
对象的保留计数变为零,并且 MyTimer
被释放。然后弱引用weakSelf
设置为nil.
然后块执行。它从 weakSelf
创建 strongSelf
。由于 weakSelf
现在为零,因此 strongSelf
也为零。所以 strongSelf.group
和 strongSelf.callback
都是 return nil,你将这些 nils 传递给 dispatch_group_async
。糟糕的节目!没有饼干!
要修复,请在创建后立即检查 strongSelf
。如果为零,则 return.
我还注意到您在 MyTimer
中没有 dealloc
方法。我怀疑恢复的 GCD 定时器被保留,所以当你的 MyTimer
被释放时它不会是 invalidated/released。您可能应该添加一个 dealloc
来执行 [self invalidate]
.