如何安全而又優(yōu)雅的使用一個NSTimer?
請往下看??
使用NSTimer
最大的困擾就是在于必須手動釋放掉這個被我們創(chuàng)建的timer
妆够,然而由于我們的粗心,很大可能忘記了去手動釋放
[self.timer invalidate];
self.timer = nil;
然而有時候我們卻忘記了該在什么時機去釋放呢?
- 我們很聰明的在dealloc中寫下代碼负蚊,自以為完美無缺的代碼
- (void)dealloc {
[self.timer invalidate];
self.timer = nil;
}
too young too simple神妹,在dealloc
中添加
NSLog(@"======= ViewController dealloc =======");
神奇的發(fā)現(xiàn),居然沒有打印家妆,wtf ?? ??
于是乎鸵荠,各種面向搜索引擎,找到了本篇文章~
如何安全而又優(yōu)雅的使用NSTimer
先來分析下引起循環(huán)印用的原因:
假設我們在viewController
中寫下這樣的代碼時
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(increaseLabel) userInfo:nil repeats:YES];
此時 timer->self->timer
揩徊,self
等timer
釋放掉才能釋放腰鬼, 而timer
需要等self
才能釋放嵌赠,如此一來塑荒,造成循環(huán)引用,相互等待釋放姜挺,最后齿税,誰也沒有釋放掉~
思考中~??,bingo??
既然dealloc
中不能寫炊豪,那我在viewDidDisappear:
中寫可以不凌箕,試試~
測試中~...
??看到控制臺輸出一段字母ViewController dealloc
,頓時心情很是愉悅??
這樣的話词渤,這個循環(huán)引用的問題不就解決了么~牵舱,好吧,就這樣了??
too young too simple~
這樣釋放掉timer
會存在一個潛在的問題缺虐,假如當前這個頁面的timer
是在做一個模塊停留計時的動作
假設是這樣子的:
用戶從A進入B模塊芜壁,此時B開始計時,
用戶從B的首頁BHomeViewController
進入到BSubViewController
時,timer
就已經停掉了
再從B返回到A時慧妄,此時的計時時間僅僅只有停留在BHomeViewController
的時間
這樣的結果顷牌,肯定不是我們想要的,如果在此基礎上塞淹,想要得到一個較準確的計時時間窟蓝,我們就必須加上一些(多余的)標記代碼,來記錄需要的一些狀態(tài)饱普,想一想运挫,axb,真夠low的??
于是乎套耕,各種面向搜索引擎滑臊,還是找到了本篇文章~
如何安全而又優(yōu)雅的使用NSTimer
此處直接貼代碼,代碼不復雜箍铲,相信各位看官都看的懂
NSTimer+Secure.h
#import <Foundation/Foundation.h>
@interface NSTimer (Secure)
/// 默認 repeats
+ (instancetype _Nullable)seScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id _Nonnull)aTarget selector:(SEL _Nonnull)aSelector;
+ (instancetype _Nullable)seScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id _Nonnull)aTarget selector:(SEL _Nonnull)aSelector repeats:(BOOL)yesOrNo;
/// 默認 repeats
+ (instancetype _Nullable)seScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id _Nonnull)aTarget block:(void (^_Nonnull)(void))block;
+ (instancetype _Nullable)seScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id _Nonnull)aTarget repeats:(BOOL)yesOrNo block:(void (^_Nonnull)(void))block;
@end
NSTimer+Secure.m
#import "NSTimer+Secure.h"
@interface SeTimerTarget: NSObject
@property (nonatomic, weak) id target;
@property (nonatomic, assign) SEL selector;
@property (nonatomic, weak) NSTimer *timer;
@property (nonatomic, copy) void(^block)(void);
@end
@implementation SeTimerTarget
- (void)seTimerTargetAction:(NSTimer *)timer {
if (self.target) {
IMP imp = [self.target methodForSelector:self.selector];
void (*func)(id, SEL, NSTimer*) = (void *)imp;
func(self.target, self.selector, timer);
}
else {
[self.timer invalidate];
self.timer = nil;
}
}
- (void)seTimerBlockAction:(NSTimer *)timer {
if (self.target && self.block) {
self.block();
}
else {
[self.timer invalidate];
self.timer = nil;
}
}
- (void)dealloc {
NSLog(@"==== timer dealloc ====");
}
@end
@implementation NSTimer (Secure)
+ (instancetype)seScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector {
return [self seScheduledTimerWithTimeInterval:ti target:aTarget selector:aSelector repeats:YES];
}
+ (instancetype)seScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector repeats:(BOOL)yesOrNo {
SeTimerTarget *timerTarget = [[SeTimerTarget alloc] init];
NSTimer *timer = [NSTimer timerWithTimeInterval:ti target:timerTarget selector:@selector(seTimerTargetAction:) userInfo:nil repeats:yesOrNo];
timerTarget.target = aTarget;
timerTarget.selector = aSelector;
timerTarget.timer = timer;
[[NSRunLoop mainRunLoop] addTimer:timerTarget.timer forMode:NSRunLoopCommonModes];
return timerTarget.timer;
}
+ (instancetype)seScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget block:(void (^)(void))block {
return [self seScheduledTimerWithTimeInterval:ti target:aTarget repeats:YES block:block];
}
+ (instancetype)seScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget repeats:(BOOL)yesOrNo block:(void (^)(void))block {
SeTimerTarget *timerTarget = [[SeTimerTarget alloc] init];
timerTarget.block = block;
timerTarget.target = aTarget;
NSTimer *timer = [NSTimer timerWithTimeInterval:ti target:timerTarget selector:@selector(seTimerBlockAction:) userInfo:nil repeats:yesOrNo];
timerTarget.timer = timer;
[[NSRunLoop mainRunLoop] addTimer:timerTarget.timer forMode:NSRunLoopCommonModes];
return timerTarget.timer;
}
@end
也可以在GitHub上去下載我的測試demo
從上面NSTimer+Secure.h
中可以看出雇卷,作者提供了兩種NSTimer
事件回調方式,target-action
和block
在使用中颠猴,也是很方便
[NSTimer seScheduledTimerWithTimeInterval:0.1 target:self selector:@selector(timerAction) repeats:YES];
或者
__weak typeof(self) weakSelf = self;
[NSTimer seScheduledTimerWithTimeInterval:0.1 target:self block:^{
[weakSelf timerAction];
}];
這樣使用NSTimer
关划,就完美解決以上所說的問題,也無需關心timer
和viewController
誰先釋放的問題
安全而又優(yōu)雅的code
??
demo地址
以上是本篇分享的所有內容翘瓮,希望對你有所幫助??~