在如今不少app中,咱們常常會看到輪播圖,輪播廣告等等,好比淘寶、京東商城app,他們均可以定時循環地播放廣告、圖片,背後的功臣之一就是今天的主角——定時器 NSTimer。bash
簡單地介紹了它的應用場景,接下來,說說這次要分享的技能點:app
###定時開始:oop
我建立一個
HomeViewController
,而後讓他成爲導航控制器的RootViewController
,在HomeViewController
的viewDidLoad
調用定時器方法,以下代碼:學習
#import "HomeViewController.h"
@interface HomeViewController ()
@end
@implementation HomeViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self regularTime];
}
/*
定時器的常規用法
*/
- (void)regularTime
{
//自動開啓
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
}
- (void)timerAction
{
NSLog(@"定時器:%s",__func__);
}
@end
複製代碼
運行結果:每隔一秒就打印一次。 ui
複製代碼
還有另一種方式也能夠開啓定時器,那就是調用這個方法:
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
spa
**注意1:**單獨地寫這一句代碼,默認是不會開啓定時器的,讓咱們看看蘋果官方原文是怎麼說的:「
You must add the new timer to a run loop, using addTimer:forMode:. Then, after ti seconds have elapsed, the timer fires, sending the message aSelector to target. (If the timer is configured to repeat, there is no need to subsequently re-add the timer to the run loop.)
」 也就是說,須要添加到runloop中,手動開啓。運行的結果同上。線程
**注意2:**在主線程中,runloop是默認開啓,若是是在子線程中開啓開啓定時器,那麼咱們還須要手動開啓runloop。運行的結果同上。code
注意3: NSRunLoopCommonModes和NSDefaultRunLoopMode優先級使用場景不一樣:通常都是默認模式。cdn
scheduledTimerWithTimeInterval
方法時,此時Timer會被加入到當前線程的RunLoop中,且模式是默認的NSDefaultRunLoopMode
,若是當前線程就是主線程,也就是UI線程時,某些UI事件,好比UIScrollView的拖動操做,會將RunLoop切換成NSEventTrackingRunLoopMode
模式,在這個過程當中,默認的NSDefaultRunLoopMode
模式中註冊的事件是不會被執行的,也就是說,此時使用scheduledTimerWithTimeInterval
添加到RunLoop中的Timer就不會執行。NSRunLoopCommonModes
,這個模式等效於NSDefaultRunLoopMode
和NSEventTrackingRunLoopMode
的結合。(參考官方文檔) 代碼以下所示:- (void)viewDidLoad {
[super viewDidLoad];
//在主線程中開啓定時器
[self regularTime];
//在子線程中開啓定時器
// [NSThread detachNewThreadWithBlock:^{
// NSLog(@"%@",[NSThread currentThread]);
// [self regularTime];
// }];
}
- (void)regularTime
{
timer = [NSTimer timerWithTimeInterval:1.0f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
//runloop中添加定時器
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
//在子線程中啓用定時器必須開啓runloop
// [[NSRunLoop currentRunLoop] run];
}
- (void)timerAction
{
NSLog(@"定時器:%s",__func__);
}
複製代碼
####fire方法blog
簡單說說fire方法,fire是火焰的意思,從字面意思能夠聯想到燃料、加速的意思,that’s right!
[timer fire]
——>就是加速計時的意思,咱們經過好比點擊事件,來讓定時器人爲地加速計時,這個比較簡單,這裏就很少贅述。
####GCD定時器
GCD定時器,經過建立隊列、建立資源來實現定時的功能,以下代碼所示: **注意:**若是延遲2秒纔開啓定時器,那麼
dispatch_resume(gcdTimer)
必須寫在外面。
#import "HomeViewController.h"
@interface HomeViewController ()
{
NSTimer * timer;
}
@end
@implementation HomeViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self gcdTimer:1 repeats:YES];
}
- (void)gcdTimer:(int)timerInterVal repeats:(BOOL)repeat
{
//建立隊列
dispatch_queue_t queue = dispatch_queue_create("my queue", 0);
//建立資源
dispatch_source_t gcdTimer = dispatch_source_create(&_dispatch_source_type_timer, 0, 0, queue);
dispatch_source_set_timer(gcdTimer,dispatch_time(DISPATCH_TIME_NOW, 0),1*NSEC_PER_SEC,0);
dispatch_source_set_event_handler(gcdTimer, ^{
if (repeat) {
NSLog(@"重複了");
[self timerAction];
} else
{
// [self timerAction];
// //調用這個方法,釋放定時器
// dispatch_source_cancel(gcdTimer);
//延遲兩秒會出現什麼狀況呢?
/*
爲什麼會調用兩次?2秒以後再觸發定時器後,耽擱了0.001秒去cancel,那定時器已經再次
觸發了,因此走了兩次,解決的方法就是把cancel寫在外面。
*/
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)
(timerInterVal*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self timerAction];
});
dispatch_source_cancel(gcdTimer);
}
});
dispatch_resume(gcdTimer);
}
/*
定時器的常規用法
*/
- (void)regularTime
{
//自動開啓
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector
(timerAction) userInfo:nil repeats:YES];
}
- (void)timerAction
{
NSLog(@"定時器:%s",__func__);
}
複製代碼
#####定時器循環引用的解決思路
- 循環引用出現的場景: eg:有兩個控制器A和B,A 跳轉到B中,B開啓定時器,可是當我返回A界面時,定時器依然還在走,控制器也並無執行dealloc方法銷燬掉。
複製代碼
先說簡單的解決思路: 蘋果官方爲了給咱們解決這個循環引用的問題,提供了一個定時器的新的自帶方法:
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;
代碼以下:
- (void)regularTime
{
//用蘋果自帶的方法,使用weakself就能夠解決定時器循環引用問題
__weak typeof(self)weakSelf = self;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0f repeats:YES block:^(NSTimer * _Nonnull timer) {
[weakSelf timerAction];
}];
}
複製代碼
第二種思路:
- (void)regularTime
{
//自動開啓
//timer置爲nil或者__weak typeof(self)weakself = self也沒法解決定時器循環引用問題
__weak typeof(self)weakself = self;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:weakself selector:
@selector(timerAction) userInfo:nil repeats:YES];
}
複製代碼
既然如此,那該如何是好? 答案是:經過類擴展,本身改寫NSTimer的類方法,在控制器中調用新的類方法,直接show the code:
複製代碼
NSTimer+HomeTimer.h中:
#import <Foundation/Foundation.h>
@interface NSTimer (HomeTimer)
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)timerInterval block:(void(^)())block repeats:(BOOL)repeat;
+ (void)timerAction:(NSTimer *)timer;
@end
複製代碼
NSTimer+HomeTimer.m中:
#import "NSTimer+HomeTimer.h"
@implementation NSTimer (HomeTimer)
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)timerInterval block:(void(^)())block repeats:(BOOL)repeat
{
return [self timerWithTimeInterval:timerInterval target:self selector:@selector(timerAction:) userInfo:block repeats:YES];
}
+ (void)timerAction:(NSTimer *)timer
{
void (^block)() = [timer userInfo];
if (block) {
block();
}
}
@end
複製代碼
類擴展寫好以後,在控制器中調用,重寫類方法,讓定時器對NSTimer類強引用,類是沒有內存空間的,就沒有循環引用,跟蘋果提供的新方法是相似的處理方式,以下代碼和運行結果所示:
#import "HomeTimerViewController.h"
#import "NSTimer+HomeTimer.h"
@interface HomeTimerViewController ()
{
NSTimer * timer;
}
@end
@implementation HomeTimerViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self regularTime];
self.view.backgroundColor = [UIColor greenColor];
}
- (void)regularTime
{
__weak typeof(self)weakSelf = self;
timer = [NSTimer timerWithTimeInterval:1.0f block:^{
[weakSelf timerAction];
} repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}
- (void)timerAction
{
NSLog(@"定時器:%s",__func__);
}
- (void)dealloc
{
NSLog(@"%s",__func__);
}
@end
複製代碼
###定時結束:用時2小時32分鐘 ###總結:
我以前在開發app的時候,對定時器更可能是會用的層次,通過此次的深刻學習,對定時器的原理有了更深刻的理解、認識,技術的提高,不少時候都是基礎知識的延伸,對原理理解透徹,不少東西就能夠觸類旁通,所有都通了,但願對本身和各位同道人有所幫助。