iOS之NSTimer

??????You use the NSTimer class to create timer objects or, more simply, timers. A timer waits until a certain time interval has elapsed and then fires, sending a specified message to a target object. For example, you could create an NSTimer object that sends a message to a window, telling it to update itself after a certain time interval.
???????你可以使用NSTimer類創建計時器對象。等到一定的時間間隔時計時器運行,發送一個消息到指定目標對象。例如,您可以創建一個NSTimer對象發送一個消息給窗口,告訴它一定時間間隔后更新。

1. 常用定時器的創建

當定時器創建完(不用scheduled 的,添加到 runloop 中)后,該定時器將在初始化時指定的 timeInterval 秒后自動觸發。如果 NSTimer 的觸發時間到的時候,runloop 在阻塞狀態,觸發時間就會推遲到下一個 runloop 周期。

1. scheduled 方式 (可單次執行)

  • 創建并啟動定時器。
  • 默認將定時器以 NSDefaultRunLoopMode 模式添加到運行循環。
  • 發生用戶交互的時候,定時器會被暫停。
 /*
        + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti 
                                             target:(id)aTarget 
                                           selector:(SEL)aSelector 
                                           userInfo:(id)userInfo 
                                            repeats:(BOOL)yesOrNo;

        參數:
            TimeInterval:觸發時間,單位秒
            target:定時起觸發對象
            selector:定時器響應方法
            userInfo:用戶信息
            repeats:是否重復執行,YES 每個指定的時間重復執行,NO 只執行一次
    */

    // 創建并啟動定時器
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 
                                                      target:self
                                                    selector:@selector(updateTimer:) 
                                                    userInfo:nil 
                                                     repeats:YES];

2. timer 方式

  • 創建定時器,添加到運行循環后啟動定時器。
  • 將定時器以指定的模式添加到運行循環。
/*
        + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti 
                                    target:(id)aTarget 
                                  selector:(SEL)aSelector 
                                  userInfo:(nullable id)userInfo 
                                   repeats:(BOOL)yesOrNo;

        - (void)addTimer:(NSTimer *)timer forMode:(NSString *)mode;

        NSDefaultRunLoopMode:發生用戶交互的時候,時鐘會被暫停。時鐘,網絡。          
        NSRunLoopCommonModes:發生用戶交互的時候,時鐘仍然會觸發,如果時鐘觸發方法非常耗時,
                              使用此方式時用戶操作會造成非常嚴重的卡頓。用戶交互,響應級別高。  
    */

    // 創建定時器
    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0
                                             target:self
                                           selector:@selector(updateTimer:)
                                           userInfo:nil
                                            repeats:YES];

    // 將定時器添加到運行循環
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

2. 官方給定的5個初始化定時器方法

  1. +(NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
```
- (void)viewDidLoad {
[super viewDidLoad];
//初始化一個Invocation對象
NSInvocation * invo = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:@selector(init)]];
[invo setTarget:self];
[invo setSelector:@selector(myLog)];
NSTimer * timer = [NSTimer timerWithTimeInterval:1 invocation:invo repeats:YES];
//加入主循環池中
[[NSRunLoop mainRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];
//開始循環
[timer fire];

}


2.  +(NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;

    ```
  NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1 
                                                   invocation:invo
                                                      repeats:YES];
  1. +(NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;

    NSTimer * timer = [NSTimer timerWithTimeInterval:1 
                                              target:self 
                                            selector:@selector(myLog) 
                                            userInfo:nil
                                             repeats:NO]
    
  2. +(NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;

    NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1
                                                       target:self
                                                     selector:@selector(myLog:)
                                                     userInfo:@"123"
                                                      repeats:YES]
    
  3. -(instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(id)ui repeats:(BOOL)rep

NSTimer * timer = [[NSTimer alloc]initWithFireDate:[NSDate distantPast]
interval:1
target:self
selector:@selector(myLog:)
userInfo:nil
repeats:YES];

[[NSRunLoop mainRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];
```

<font style="color:brown" size='4'>注意:</font>這五種初始化方法的異同:

  1. 參數repeats是指定是否循環執行,YES將循環,NO將只執行一次。
  2. timerWithTimeInterval這兩個類方法創建出來的對象如果不用 addTimer: forMode方法手動加入主循環池中,將不會循環執行。并且如果不手動調用fair,則定時器不會啟動。
  3. scheduledTimerWithTimeInterval這兩個方法不需要手動調用fair,會自動執行,并且自動加入主循環池。
  4. init方法需要手動加入循環池,它會在設定的啟動時間啟動。

3. 定時器的開啟暫停關閉和屬性變量

  1. 啟動定時器
[timer setFireDate:[NSDate distantPast]];
  1. 暫停定時器

    [timer setFireDate:[NSDate distantFuture]];
    
  2. 關閉定時器,永久關閉定時器

    [timer invalidate];
    
  3. 屬性變量

    • 這是設置定時器的啟動時間,常用來管理定時器的啟動與停止

      @property (copy) NSDate *fireDate;
      
      //啟動定時器
      timer.fireDate = [NSDate distantPast];
      //停止定時器
      timer.fireDate = [NSDate distantFuture];
      
    • 這個是一個只讀屬性,獲取定時器調用間隔時間。

      @property (readonly) NSTimeInterval timeInterval; 
      
      
    • 這是7.0之后新增的一個屬性,因為NSTimer并不完全精準,通過這個值設置誤差范圍

      @property NSTimeInterval tolerance;
      
    • 獲取定時器是否有效

      @property (readonly, getter=isValid) BOOL valid;
      
    • 獲取參數信息

      @property (readonly, retain) id userInfo;
      

4. 在子線程中創建定時器

//  創建子線程
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        // 在子線程創建定時器
        /*
         scheduled 或 timer 方式創建
         */
        self.timer = [NSTimer timerWithTimeInterval:1.0
                                             target:self
                                           selector:@selector(updateTimer:)
                                           userInfo:nil
                                            repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
        
        // 啟動子線程的運行循環
        /*
         這句代碼就是一個死循環!如果不停止運行循環,不會執行添加到此句之后的 任何代碼
         */
        CFRunLoopRun();
                
        // 停止子線程運行循環之前,不會執行添加到此處的任何代碼
    });
    
    
// 定時器執行操作方法
- (void)updateTimer {

        static int num = 0;
        num++;

        // 滿足條件后,停止當前的運行循環
        if (num == 8) {

            // 停止當前的運行循環
            /*
                一旦停止了運行循環,后續代碼能夠執行,執行完畢后,線程被自動銷毀
            */
            CFRunLoopStop(CFRunLoopGetCurrent());
        }
    }

5. 定時器的銷毀

在官方文檔中我們可以看到 [timer invalidate]是唯一的方法將定時器從循環池中移除。

如果我們啟動了一個定時器,在某個界面釋放前,將這個定時器停止,甚至置為nil,都不能是這個界面釋放,原因是系統的循環池中還保有這個對象,我們需要在下面三個方法中

  • 定時器執行方法

  • 視圖顯示ViewDidDisappera方法

  • deallock 方法

實現如下代碼進行定時器的徹底釋放:

    if (timer.isValid) {
        [timer invalidate];
    }
    timer=nil;

6. 其他實現定時功能的方法

  1. performSelector 延時調用

    /*
        1.5 秒后自動調用 self 的 hideHUD 方法
    */
    [self performSelector:@selector(hideHUD) withObject:nil afterDelay:1.5];
    
    // 取消延時調用
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideHUD) object:nil];
    
  2. GCD 多線程

    
    /*
        1.5 秒后自動執行 block 里面的代碼
    */
     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    
        self.hud.alpha = 0.0;
        });
    

3. NSTimer 定時器

    ```
    /*
        1.5 秒后自動調用 self 的 hideHUD 方法
    */
    [NSTimer scheduledTimerWithTimeInterval:1.5 
                                     target:self 
                                   selector:@selector(hideHUD)
                                   userInfo:nil 
                                    repeats:NO];
    ```



最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容