iOS NSTimer 的全部應用(一)

NSTimer的使用總結為三要素吧:時間間隔、被觸發(fā)、發(fā)送消息(執(zhí)行方法)

  • 它會被添加到runloop,否則不會運行,當然添加的runloop不存在也不會運行;
  • 還要指定添加到的runloop的哪個模式,而且還可以指定添加到runloop的多個模式,模式不對也是不會運行的
  • runloop會對timer有強引用,timer會對目標對象進行強引用(是否隱約的感覺到坑了。。。)
  • timer的執(zhí)行時間并不準確,系統(tǒng)繁忙的話,還會被跳過去
  • invalidate調用后,timer停止運行后,就一定能從runloop中消除嗎,資源????

系統(tǒng)提供了8個創(chuàng)建方法,6個類創(chuàng)建方法,2個實例初始化方法。
有三個方法直接將timer添加到了當前runloop default mode,而不需要我們自己操作,當然這樣的代價是runloop只能是當前runloop,模式是default mode:

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
 
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
 
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;

下面五種創(chuàng)建,不會自動添加到runloop,還需調用addTimer:forMode:

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;
 
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
 
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
 
- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(id)ui repeats:(BOOL)rep;
 
- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;

對上面所有方法參數做個說明:

  • ti(interval):定時器觸發(fā)間隔時間,單位為秒,可以是小數。如果值小于等于0.0的話,系統(tǒng)會默認賦值0.1毫秒
  • invocation:這種形式用的比較少,大部分都是block和aSelector的形式
  • yesOrNo(rep):是否重復,如果是YES則重復觸發(fā),直到調用invalidate方法;如果是NO,則只觸發(fā)一次就自動調用invalidate方法
  • aTarget(t):發(fā)送消息的目標,timer會強引用aTarget,直到調用invalidate方法
  • aSelector(s):將要發(fā)送給aTarget的消息,如果帶有參數則應:- (void)timerFireMethod:(NSTimer *)timer聲明
  • userInfo(ui):傳遞的用戶信息。使用的話,首先aSelector須帶有參數的聲明,然后可以通過[timer userInfo]獲取,也可以為nil,那么[timer userInfo]就為空
  • date:觸發(fā)的時間,一般情況下我們都寫[NSDate date],這樣的話定時器會立馬觸發(fā)一次,并且以此時間為基準。如果沒有此參數的方法,則都是以當前時間為基準,第一次觸發(fā)時間是當前時間加上時間間隔ti
  • block:timer觸發(fā)的時候會執(zhí)行這個操作,帶有一個參數,無返回值

添加到runloop,參數timer是不能為空的,否則拋出異常
- (void)addTimer:(NSTimer *)timer forMode:(NSRunLoopMode)mode;

另外,系統(tǒng)提供了一個- (void)fire;方法,調用它可以觸發(fā)一次:

  • 對于重復定時器,它不會影響正常的定時觸發(fā)
  • 對于非重復定時器,觸發(fā)后就調用了invalidate方法,既使正常的還沒有觸發(fā)

NSTimer添加到NSRunLoop
如同引言中說的那樣,``timer必須添加到runloop才有效,很明顯要保證兩件事情,一是runloop存在(運行),另一個才是添加。確保這兩個前提后,還有runloop`模式的問題。

一個timer可以被添加到runloop的多個模式,比如在主線程中runloop一般處于NSDefaultRunLoopMode,而當滑動屏幕的時候,比如UIScrollView或者它的子類UITableView、UICollectionView等滑動時runloop處于UITrackingRunLoopMode模式下,因此如果你想讓timer在滑動的時候也能夠觸發(fā),就可以分別添加到這兩個模式下。或者直接用NSRunLoopCommonModes一個模式集,包含了上面的兩種模式。

但是一個timer只能添加到一個runloop(runloop與線程一一對應關系,也就是說一個timer只能添加到一個線程)。如果你非要添加到多個runloop,則只有一個有效

invalidate方法有2個功能:一是將timer從runloop中移除,那么圖中的L4就消失,二是timer本身也會釋放它持有資源,比如target、userinfo、block

  • timer不用了,一定要調用invalidate
  • 一般是target釋放的同時,才會知道timer不用了,那么怎么捕獲target被釋放了呢?dealloc方法肯定是不行的。如果是控制器的話可以嘗試監(jiān)聽pop方法的調用(nav的代理),viewDidDisappear方法里面(但要記著,再次展示的時候從新添加。。。)
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容