1.問題描述
使用NSTimer,repeats引數傳YES的時候,可能導致Timer不能及時釋放,如:
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:weakSelf
selector:@selector(timerTick:)
userInfo:nil
repeats:YES];
複製程式碼
此時,假如持有timer的是UIView物件self,self不能呼叫Dealloc釋放。所以,在需要釋放self的時候,需要先手動銷燬計時器timer。
- (void)dealloc {
//即使將timer的target設定為weakSelf,也不會進入此方法,進行釋放
[self destroyTimer];
}
複製程式碼
2.使用GCD計時器替換NSTimer可以解決以上問題
標頭檔案
#import <Foundation/Foundation.h>
@interface TTTimer : NSObject
/** 計時器總時長 */
@property (nonatomic, assign) NSInteger duration;
/** 鐘擺間隔 */
@property (nonatomic, assign) NSInteger tickInterval;
/**
倒數計時
*/
- (void)tickDownProgress:(void(^)(void))progress;
- (void)tickDownCompletion:(void(^)(void))completion;
- (void)suspend;
- (void)resume;
- (void)destroy;
@end
複製程式碼
實現檔案
#import "TTTimer.h"
@interface TTTimer () {
BOOL _isTickComplete;
BOOL _isSuspend;
}
@property (nonatomic, strong) dispatch_source_t timer;
@end
@implementation TTTimer
- (instancetype)init {
if (self = [super init]) {
_duration = 0;
_tickInterval = 0;
_isTickComplete = NO;
_isSuspend = YES;
}
return self;
}
- (void)tickDownProgress:(void(^)(void))progress {
if(self.duration == 0) return;
if (!self.timer) {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(self.timer,
dispatch_walltime(NULL, 0),
self.tickInterval * NSEC_PER_SEC,
0);
dispatch_source_set_event_handler(self.timer, ^{
if (progress) {
dispatch_async(dispatch_get_main_queue(), ^{
progress();
});
}
});
[self resume];
}
}
- (void)tickDownCompletion:(void(^)(void))completion {
if(self.duration == 0) return;
__block NSInteger timeOut = self.duration;
if (!self.timer) {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(self.timer,
dispatch_walltime(NULL, 0),
self.tickInterval * NSEC_PER_SEC,
0);
dispatch_source_set_event_handler(self.timer, ^{
if (timeOut <= 0) {
[self destroy];
if(completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
}
_isTickComplete = YES;
}else {
timeOut = timeOut - self.tickInterval;
}
});
[self resume];
}
}
- (void)suspend {
if (self.timer && !_isSuspend) {
dispatch_suspend(self.timer);
_isSuspend = YES;
}
}
- (void)resume {
if (self.timer && _isSuspend) {
dispatch_resume(self.timer);
_isSuspend = NO;
}
}
- (void)destroy {
if (self.timer) {
if (!_isTickComplete) {
[self resume];
}
dispatch_source_cancel(self.timer);
self.timer = nil;
}
}
@end
複製程式碼
- 避免Block迴圈引用
- (void)setupTimer {
__weak typeof(self) weakSelf = self;
self.toolTimer = [[TTTimer alloc] init];
self.toolTimer.tickInterval = 1;
self.toolTimer.duration = 5;
[self.toolTimer tickDownCompletion:^{
[weakSelf doSomething];
}];
}
複製程式碼