一、主線程開啟NSTimer
開啟方式:scheduledTimerWithTimeInterval(直接開啟)與timerWithTimeInterval(需要加入到RunLoop中進(jìn)行開啟)兩種開啟方式炒嘲。
加入RunLoop中的開啟方式:
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
二脐区、子線程中開啟定時器
timerWithTimeInterval在線程中開啟定時器需要在RunLoop中run,在主線中默認(rèn)開啟了撩幽。為什么我在退出控制器后隘击,控制器不執(zhí)行dealloc方法替久?
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[self.timer invalidate];
}
- (void)viewDidLoad {
[super viewDidLoad];
[NSThread detachNewThreadWithBlock:^{
[self regularTimer1];
}];
}
- (void)regularTimer1{
_timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode]
[[NSRunLoop currentRunLoop] run];
}
三脊串、定時器循環(huán)引用的問題
當(dāng)scheduledTimerWithTimeInterval使用創(chuàng)建定時器的時候辫呻,一定要在viewWillDisappear中將定時器從runloop中移除清钥,否則當(dāng)退出控制器的時候不能執(zhí)行dealloc方法。疑點(diǎn):如果使用timerWithTimeInterval(非block情況下)在runloop中添加定時器放闺,使用invalidate方法祟昭,退出控制器后不能執(zhí)行dealloc方法。
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[self.timer invalidate];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self regularTimer];
}
- (void)regularTimer{
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
}
- (void)timerAction{
NSLog(@"%s",__func__);
}
如何在使用timerWithTimeInterval(非block情況下)時怖侦,防止強(qiáng)引用呢篡悟?對NSTimer使用類別,讓timerWithTimeInterval不持有self對象匾寝。
#import <Foundation/Foundation.h>
@interface NSTimer (MyTimer)
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)timeInterval block:(void(^)())block repeats:(BOOL)repeat;
+ (void)timerAction:(NSTimer *)timer;
@end
#import "NSTimer+MyTimer.h"
@implementation NSTimer (MyTimer)
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)timeInterval block:(void(^)())block repeats:(BOOL)repeat{
//1搬葬、類方法不能調(diào)用成員方法
//2、對self的強(qiáng)引用是類艳悔,類不占用內(nèi)存空間
return [self timerWithTimeInterval:timeInterval target:self selector:@selector(timerAction:) userInfo:block repeats:repeat];
}
+ (void)timerAction:(NSTimer *)timer{
NSLog(@"%@",[timer userInfo]);
void (^block)() = [timer userInfo];
if (block) {
block();
}
}
@end
在控制器中使用NSTimer類別中的+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)timeInterval block:(void(^)())block repeats:(BOOL)repeat;方法創(chuàng)建定時器急凰。
- (void)dealloc{
//1、self 的引用計(jì)數(shù)器為0很钓,自動執(zhí)行dealloc
//2香府、如果不執(zhí)行invalidate,定時還會在runloop中,NSTimer+MyTimer.h中的timerAction會一直執(zhí)行
[_timer invalidate];
//1码倦、可直接置空企孩,當(dāng)置空后,timer立即被釋放被釋放袁稽,如果不置空勿璃,在等執(zhí)行dealloc后.
//2、_timer = nil與self.timer = nil效果一樣推汽,具體以后再更新說明补疑。
//self.timer = nil 的寫法
/*
- (void)setTimer:(NSTimer *)timer{
if(_timer != timer){
[_timer release];
_timer = [timer retain];
}
}
*/
self.timer = nil;
NSLog(@"%s",func);
} - (void)viewDidLoad {
[super viewDidLoad];
[self regularTimer];
} - (void)regularTimer{
__weak typeof(self) weakSelf = self;
_timer = [NSTimer timerWithTimeInterval:1.0f block:^{
//循環(huán)引用 self->timer->block->self
[weakSelf timerAction];
} repeats:YES];
[[NSRunLoop currentRunLoop]addTimer:_timer forMode:NSDefaultRunLoopMode];
} - (void)timerAction{
NSLog(@"%s",func);
}
在iOS10中已提供如下API來防止使用timerWithTimeInterVal(非block情況下)對self產(chǎn)生的循環(huán)引用。
- (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
四歹撒、使用GCD來啟動定時器
- (void)timerAction{
NSLog(@"%s",func);
}
/**
使用多線程創(chuàng)建定時器
@param timerInterVal 時間間隔
@param repeat 是否重復(fù)
*/ - (void)gcdTimer:(int)timerInterVal repeats:(BOOL)repeat{
//自定義一個隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("timeQueue", DISPATCH_QUEUE_SERIAL);
//創(chuàng)建定時器
dispatch_source_t gcdTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(gcdTimer, DISPATCH_TIME_NOW, timerInterVal * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(gcdTimer, ^{
NSLog(@"dispatch_source_set_event_handler");
if (repeat) {
[self timerAction];
}else{
dispatch_source_cancel(gcdTimer);
//間隔timerInterVal后執(zhí)行block里面的方法
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timerInterVal * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"dispatch_after");
[self timerAction];
//在這里取消GCD莲组,但是會執(zhí)行兩次(dispatch_after過了timerInterVal秒,定時器的任務(wù)還沒有取消之前又開始執(zhí)行了)
//dispatch_source_cancel(gcdTimer);
});
}
});
dispatch_resume(gcdTimer);
}
五暖夭、在不同RunLoop模式下創(chuàng)建定時器
使用timerWithTimeInterval方法锹杈,需要添加到runloop中才能開啟定時器,加入定時器有五種模式:
1.NSDefaultRunLoopMode迈着,默認(rèn)模式竭望。執(zhí)行scheduledTimerWithTimeInterval方法,它會自動加入到該模式下裕菠。
2.NSConnectionReplyMode咬清,系統(tǒng)內(nèi)部使用,用戶基本不會使用。
3.NSModalPanelRunLoopMode,處理modal panels事件旧烧,這是嘛事件影钉?
4.NSEventTrackingRunLoopMode,UIScrollView粪滤,UITableView斧拍,UICollectionView 滑動是runloop處于此模式下雀扶。
5.NSRunLoopCommonModes,普通模式杖小,防止NSEventTrackingRunLoopMode與NSDefaultRunLoopMode之間的切換,可以把定時器加入普通模式下愚墓。