NSTimer(計(jì)時(shí)器)是一種很方便很有用的對象岛请,可以指定絕對的日期與時(shí)間压怠,以便到時(shí)執(zhí)行任務(wù)掐场,也可以指定執(zhí)行執(zhí)行任務(wù)的相對延遲時(shí)間,還可以重復(fù)運(yùn)行任務(wù)旋膳,設(shè)定“間隔值”用來指定任務(wù)的觸發(fā)頻率澎语。
計(jì)時(shí)器要和運(yùn)行循環(huán)相關(guān)聯(lián),運(yùn)行循環(huán)到時(shí)候會觸發(fā)任務(wù)。只有把計(jì)時(shí)器放到運(yùn)行循環(huán)里咏连,它才能正常觸發(fā)任務(wù)盯孙。例如,下面這個(gè)方法可以創(chuàng)建計(jì)時(shí)器祟滴,并將其預(yù)先安排在當(dāng)前運(yùn)行循環(huán)中:
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
此方法創(chuàng)建出來的計(jì)時(shí)器,會在指定的間隔時(shí)間之后執(zhí)行任務(wù)振惰。也可以令其反復(fù)執(zhí)行任務(wù),直到開發(fā)者稍后將其手動關(guān)閉為止垄懂。target和selector表示在哪個(gè)對象上調(diào)用哪個(gè)方法骑晶。執(zhí)行完任務(wù)后,一次性計(jì)時(shí)器會失效草慧,若repeats為YES桶蛔,那么必須調(diào)用invalidate方法才能使其停止。
重復(fù)執(zhí)行模式的計(jì)時(shí)器漫谷,很容易引入保留環(huán):
@interface EOCClass : NSObject
- (void)startPolling;
- (void)stopPolling;
@end
@implementation EOCClass{
NSTimer *_poliTimer;
}
- (id) init{
return [super init];
}
- (void)dealloc{
[_poliTimer invalidate];
}
- (void)stopPolling{
[_poliTimer invalidate];
_poliTimer = nil;
}
- (void)startPolling{
_poliTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(p_doPoll) userInfo:nil repeats:YES];
}
- (void)p_doPoll{
// code
}
如果創(chuàng)建了本類實(shí)例仔雷,并調(diào)用了startPolling方法。創(chuàng)建計(jì)時(shí)器的時(shí)候舔示,由于目標(biāo)對象是self碟婆,所以要保留此實(shí)例。然而惕稻,因?yàn)橛?jì)時(shí)器是用實(shí)例變量存放的竖共,所以實(shí)例也保留了計(jì)數(shù)器,于是就產(chǎn)生了保留環(huán)俺祠。
調(diào)用stopPolling方法或令系統(tǒng)將實(shí)例回收(會自動調(diào)用dealloc方法)可以使計(jì)時(shí)器失效公给,從而打破循環(huán),但無法確保stopPolling方法一定調(diào)用蜘渣,而由于計(jì)時(shí)器保存著實(shí)例淌铐,實(shí)例永遠(yuǎn)不會被系統(tǒng)回收。當(dāng)EOCClass實(shí)例的最后一個(gè)外部引用移走之后蔫缸,實(shí)例仍然存活腿准,而計(jì)時(shí)器對象也就不可能被系統(tǒng)回收,除了計(jì)時(shí)器外沒有別的引用再指向這個(gè)實(shí)例捂龄,實(shí)例就永遠(yuǎn)丟失了,造成內(nèi)存泄漏加叁。
解決方案是采用塊為計(jì)時(shí)器添加新功能:
@interface NSTimer (EOCBlocksSupport)
+ (NSTimer*)eoc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)repeats;
@end
@implementation NSTimer( EOCBlocksSupport)
+ (NSTimer*)eoc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void (^)())block repeats:(BOOL)repeats{
return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(eoc_blockInvoke:) userInfo:[block copy] repeats:repeats];
}
+ (void)eoc_blockInvoke:(NSTimer*)timer{
void (^block)() = timer.userInfo;
if (block) {
block();
}
}
再修改stopPolling方法:
- (void)startPolling{
__weak EOCClass *weakSelf = self;
_poliTimer = [NSTimer eoc_scheduledTimerWithTimeInterval:5.0 block:^{
EOCClass *strongSelf = weakSelf;
[strongSelf p_doPoll];
} repeats:YES];
}
這段代碼先定義了一個(gè)弱引用指向self倦沧,然后用塊捕獲這個(gè)引用,這樣self就不會被計(jì)時(shí)器所保留它匕,當(dāng)塊開始執(zhí)行時(shí)展融,立刻生成strong引用,保證實(shí)例在執(zhí)行器繼續(xù)存活豫柬。