利用類擴充套件解決NSTimer會保留目標物件
阿新 • • 發佈:2020-06-24
場景
在程式開發中經常會遇到計時器,比如促銷活動的倒計時,傳送簡訊驗證碼過段時間才允許第二次傳送、設定一段倒計時。
問題
這時會用到 下面這個api,
scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: API_AVAILABLE(ios(2.0))
,這個api從ios 2.0就開始提供了,可以相容較低版本的ios系統。可是由於Timer會保留target引數,所以這個api會比較容易造成迴圈引用。想象一下有一個自定義ViewController1,裡邊有個倒計時功能,很容易通過下邊的程式碼實現:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(invokeTimer) userInfo:nil repeats:YES];
}
- (void)invokeTimer {
NSLog(@"%@" ,self);
}
- (void)dealloc
{
[_timer invalidate];
NSLog(@"%s",__FUNCTION__);
}
複製程式碼
每過一秒鐘觸發一次,觸發方法會輸出log,在dealloc中將timer置為無效。看似天衣無縫,但實際上,從自定義ViewController1返回時,並不會呼叫dealloc,原因就是上文中提到的NSTimer對target的保留。這樣就會造成ViewController1的記憶體洩漏了。
解決思路
利用像下邊這樣的類擴充套件,此時Timer的target 不再是自定義ViewController1
typedef void (^TimerHandler) (NSTimer *);
@interface NSTimer (HandleRetainTarget)
@end
@implementation NSTimer(HandleRetainTarget)
+ (NSTimer *)Eoc_timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))handler {
return [NSTimer scheduledTimerWithTimeInterval:interval target:[self class] selector:@selector(repeatTimer:) userInfo:[handler copy] repeats:YES];
}
+ (void)repeatTimer:(NSTimer *)timer {
TimerHandler handler = timer.userInfo;
if (handler) {
handler(timer);
}
}
@end
複製程式碼
呼叫示例
__weak typeof (self) weakself = self;
_timer = [NSTimer Eoc_timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer *timer) {
[weakself invokeTimer];
}];
複製程式碼
截圖
點選Button -> 點選Back -> 檢視log
! |
---|