溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

iOS中如何使用各種定時器

發(fā)布時間:2021-08-04 11:40:32 來源:億速云 閱讀:127 作者:小新 欄目:移動開發(fā)

這篇文章將為大家詳細(xì)講解有關(guān)iOS中如何使用各種定時器,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

一. NSTimer

NSTimer的初始化方法有以下幾種:

會自動啟動, 并加入 MainRunloop 的 NSDefaultRunLoopMode 中,

注意: 這里的自動啟動, 并不是馬上就會啟動, 而是會延遲大概一個interval的時間:

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block

參數(shù):

  • internal : 時間間隔, 多久調(diào)用一次

  • repeats: 是否重復(fù)調(diào)用

  • block: 需要重復(fù)做的事情

使用:

 [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
 
 static NSInteger num = 0;
 
 NSLog(@"%ld", (long)num);
 num++;
 
 if (num > 4) {
 
 [timer invalidate];
 
 NSLog(@"end");
 }
 }];
 
 NSLog(@"start");

這時, 控制臺的輸出:

2016-12-29 16:29:53.901 定時器[11673:278678] start
2016-12-29 16:29:54.919 定時器[11673:278678] 0
2016-12-29 16:29:55.965 定時器[11673:278678] 1
2016-12-29 16:29:56.901 定時器[11673:278678] 2
2016-12-29 16:29:57.974 定時器[11673:278678] 3
2016-12-29 16:29:58.958 定時器[11673:278678] 4
2016-12-29 16:29:58.959 定時器[11673:278678] end

可以看出, 這里的internal設(shè)置為1s, 大概延遲了1s才開始執(zhí)行block里的內(nèi)容;

這里的停止定時器, 我直接在block里進(jìn)行的, 如果使用一個全局變量來再其他地方手動停止定時器,需要這樣進(jìn)行:

[self.timer invalidate];
self.timer = nil;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo

參數(shù):

  • ti: 重復(fù)執(zhí)行時間間隔

  • invocation: NSInvocation實例, 其用法見NSInvocation的基本用法

  • yesOrNo: 是否重復(fù)執(zhí)行

示例:

// NSInvocation形式
- (void)timer2 {
 
 NSMethodSignature *method = [ViewController instanceMethodSignatureForSelector:@selector(invocationTimeRun:)];
 
 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:method];
 NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 invocation:invocation repeats:YES];
 
 // 設(shè)置方法調(diào)用者
 invocation.target = self;
 
 // 這里的SEL需要和NSMethodSignature中的一致
 invocation.selector = @selector(invocationTimeRun:);
 
 // 設(shè)置參數(shù)
 // //這里的Index要從2開始,以為0跟1已經(jīng)被占據(jù)了,分別是self(target),selector(_cmd)
 // 如果有多個參數(shù), 可依次設(shè)置3 4 5 ...
 [invocation setArgument:&timer atIndex:2];
 
 [invocation invoke];
 
 NSLog(@"start");
}
- (void)invocationTimeRun:(NSTimer *)timer {
 
 static NSInteger num = 0;
 NSLog(@"%ld---%@", (long)num, timer);
 
 num++;
 
 if (num > 4) {
 [timer invalidate];
 }
}

輸出:

2016-12-29 16:52:54.029 定時器[12089:289673] 0---<__NSCFTimer: 0x60000017d940>
2016-12-29 16:52:54.029 定時器[12089:289673] start
2016-12-29 16:52:55.104 定時器[12089:289673] 1---<__NSCFTimer: 0x60000017d940>
2016-12-29 16:52:56.095 定時器[12089:289673] 2---<__NSCFTimer: 0x60000017d940>
2016-12-29 16:52:57.098 定時器[12089:289673] 3---<__NSCFTimer: 0x60000017d940>
2016-12-29 16:52:58.094 定時器[12089:289673] 4---<__NSCFTimer: 0x60000017d940>

可以看出, 這里定時器是立馬就執(zhí)行了, 沒有延遲;

此方法可以傳遞多個參數(shù), 下面是傳遞兩個參數(shù)的示例:

// NSInvocation形式
- (void)timer2 {
 
 NSMethodSignature *method = [ViewController instanceMethodSignatureForSelector:@selector(invocationTimeRun:des:)];
 
 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:method];
 NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 invocation:invocation repeats:YES];
 
 // 設(shè)置方法調(diào)用者
 invocation.target = self;
 
 // 這里的SEL需要和NSMethodSignature中的一致
 invocation.selector = @selector(invocationTimeRun:des:);
 
 // 設(shè)置參數(shù)
 // //這里的Index要從2開始,以為0跟1已經(jīng)被占據(jù)了,分別是self(target),selector(_cmd)
 // 如果有多個參數(shù), 可依次設(shè)置3 4 5 ...
 [invocation setArgument:&timer atIndex:2];
 // 設(shè)置第二個參數(shù)
 NSString *dsc = @"第二個參數(shù)是字符串";
 [invocation setArgument:&dsc atIndex:3];
 
 [invocation invoke];
 
 NSLog(@"start");
}
- (void)invocationTimeRun:(NSTimer *)timer des:(NSString *)dsc {
 
 static NSInteger num = 0;
 NSLog(@"%ld---%@--%@", (long)num, timer, dsc);
 
 num++;
 
 if (num > 4) {
 [timer invalidate];
 }
}

輸出:

2016-12-29 16:57:45.087 定時器[12183:292324] 0---<__NSCFTimer: 0x60000016dbc0>--第二個參數(shù)是字符串
2016-12-29 16:57:45.088 定時器[12183:292324] start
2016-12-29 16:57:46.161 定時器[12183:292324] 1---<__NSCFTimer: 0x60000016dbc0>--第二個參數(shù)是字符串
2016-12-29 16:57:47.161 定時器[12183:292324] 2---<__NSCFTimer: 0x60000016dbc0>--第二個參數(shù)是字符串
2016-12-29 16:57:48.150 定時器[12183:292324] 3---<__NSCFTimer: 0x60000016dbc0>--第二個參數(shù)是字符串
2016-12-29 16:57:49.159 定時器[12183:292324] 4---<__NSCFTimer: 0x60000016dbc0>--第二個參數(shù)是字符串
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo

參數(shù):

  • ti: 時間間隔

  • aTarget: 調(diào)用者

  • aSelector: 執(zhí)行的方法

  • userInfo: 參數(shù)

  • yesOrNo: 是否重復(fù)執(zhí)行

示例:

- (void)timer3 {
 
 NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(targetRun:) userInfo:@"這是攜帶的參數(shù)" repeats:YES];
 
 NSLog(@"start");
}
- (void)targetRun:(NSTimer *)timer {
 
 static NSInteger num = 0;
 
 NSLog(@"%ld---%@--%@", (long)num, timer, timer.userInfo);
 
 num++;
 
 if (num > 4) {
 [timer invalidate];
 }
}

輸出:

2016-12-29 17:05:11.590 定時器[12328:296879] start
2016-12-29 17:05:12.655 定時器[12328:296879] 0---<__NSCFTimer: 0x608000162700>--這是攜帶的參數(shù)
2016-12-29 17:05:13.661 定時器[12328:296879] 1---<__NSCFTimer: 0x608000162700>--這是攜帶的參數(shù)
2016-12-29 17:05:14.664 定時器[12328:296879] 2---<__NSCFTimer: 0x608000162700>--這是攜帶的參數(shù)
2016-12-29 17:05:15.651 定時器[12328:296879] 3---<__NSCFTimer: 0x608000162700>--這是攜帶的參數(shù)
2016-12-29 17:05:16.650 定時器[12328:296879] 4---<__NSCFTimer: 0x608000162700>--這是攜帶的參數(shù)

下面這三種方式創(chuàng)建定時器的用法, 和上面相應(yīng)的方法類似, 需要注意的是, 這樣創(chuàng)建的定時器, 并不會執(zhí)行, 需要我們手動來開啟定時器;

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo

開啟的方式是, 將當(dāng)前定時器添加到RunLoop中:

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

下面給出一個示例:

- (void)timer4 {
 
 NSTimer *timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
 
 static NSInteger num = 0;
 
 NSLog(@"%ld", (long)num);
 num++;
 
 if (num > 4) {
 
 [timer invalidate];
 timer = nil;
 
 NSLog(@"end");
 }
 }];
 
 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
 
 NSLog(@"start");
}

輸出:

2016-12-29 17:12:13.955 定時器[12498:301751] start
2016-12-29 17:12:15.013 定時器[12498:301751] 0
2016-12-29 17:12:16.018 定時器[12498:301751] 1
2016-12-29 17:12:17.011 定時器[12498:301751] 2
2016-12-29 17:12:18.024 定時器[12498:301751] 3
2016-12-29 17:12:19.023 定時器[12498:301751] 4
2016-12-29 17:12:19.023 定時器[12498:301751] end

定時器基本的創(chuàng)建方式就這些了, 還可以設(shè)置其他的屬性, 例如開啟時間, 這些直接參考其API 進(jìn)行設(shè)置即可;

注意: 以上實例中, 我沒有使用全局的NSTimer 對象, 如果設(shè)置全局變量, 或者設(shè)置為屬性, 在停止定時器的時候要手動置為nil, 即:

[timer invalidate];
 timer = nil;

二. GCD

dispatch_after : 延遲執(zhí)行一次

dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block)

示例:

- (void)gcdTimer {
 
 // 延遲2s
 dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);
 
 dispatch_after(delayTime, dispatch_get_main_queue(), ^(void){
 
 NSLog(@"延遲2s后執(zhí)行");
 });
 
 NSLog(@"start");
}

重復(fù)執(zhí)行的定時器

void
dispatch_source_set_timer(dispatch_source_t source,
 dispatch_time_t start,
 uint64_t interval,
 uint64_t leeway)

參數(shù):

  • source: 定時器

  • start: 開始時間, 當(dāng)我們使用 dispatch_time 或者 DISPATCH_TIME_NOW 時,系統(tǒng)會使用默認(rèn)時鐘來進(jìn)行計時。然而當(dāng)系統(tǒng)休眠的時候,默認(rèn)時鐘是不走的,也就會導(dǎo)致計時器停止。使用 dispatch_walltime 可以讓計時器按照真實時間間隔進(jìn)行計時;

  • interval: 間隔(如果設(shè)置為 DISPATCH_TIME_FOREVER 則只執(zhí)行一次)

  • leeway: 允許的誤差范圍; 計時不可能是百分百精確的, 即使設(shè)置為0, 也不是百分百精確的, 所以可以設(shè)置合理的允許誤差, 單位: 納秒(NSEC_PER_SEC)

相關(guān)內(nèi)容, 可參考文章: Dispatch Source Timer 的使用以及注意事項

// 重復(fù)執(zhí)行的定時器
- (void)gcdTimer1 {
 
 // 獲取全局隊列
 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 // 創(chuàng)建定時器
 dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
 
 // 開始時間
 dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
 
// dispatch_time_t start = dispatch_walltime(NULL, 0);
 
 // 重復(fù)間隔
 uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC);
 
 // 設(shè)置定時器
 dispatch_source_set_timer(_timer, start, interval, 0);
 
 // 設(shè)置需要執(zhí)行的事件
 dispatch_source_set_event_handler(_timer, ^{
 
 //在這里執(zhí)行事件
 static NSInteger num = 0;
 
 NSLog(@"%ld", (long)num);
 num++;
 
 if (num > 4) {
 
 NSLog(@"end");
 
 // 關(guān)閉定時器
 dispatch_source_cancel(_timer);
 }
 });
 // 開啟定時器
 dispatch_resume(_timer);
 
 NSLog(@"start");
}

輸出:

2016-12-30 10:15:01.114 定時器[3393:99474] start
2016-12-30 10:15:02.187 定時器[3393:99796] 0
2016-12-30 10:15:03.114 定時器[3393:99796] 1
2016-12-30 10:15:04.186 定時器[3393:99796] 2
2016-12-30 10:15:05.188 定時器[3393:99796] 3
2016-12-30 10:15:06.188 定時器[3393:99796] 4
2016-12-30 10:15:06.188 定時器[3393:99796] end

這里的開始時間設(shè)置了1s的間隔, 所以1s之后才開始執(zhí)行,可以設(shè)置使用DISPATCH_TIME_NOW來立馬執(zhí)行;

注意:

這里的開始時間(start)可以使用下面的方式的來設(shè)置:

dispatch_time_t start = dispatch_walltime(NULL, 0);

或者直接設(shè)置為: DISPATCH_TIME_NOW

關(guān)于 dispatch_walltime 和 dispatch_time 的區(qū)別, 上面也有提及,也可參考stackOverflow上的這個回答; 主要區(qū)別就是前者在系統(tǒng)休眠時還會繼續(xù)計時, 而后者在系統(tǒng)休眠時就停止計時, 待系統(tǒng)重新激活時, 接著繼續(xù)計時;

停止計時器:

停止GCD定時器的方式, Dispatch Source Timer 的使用以及注意事項中有提及, 主要有以下兩種:

// 關(guān)閉定時器
// 完全銷毀定時器, 重新開啟的話需要重新創(chuàng)建
// 全局變量, 關(guān)閉后需要置為nil
dispatch_source_cancel(_timer);
 
// 暫停定時器
// 可使用dispatch_resume(_timer)再次開啟
// 全局變量, 暫停后不能置為nil, 否則不能重新開啟
dispatch_suspend(_timer);

三. CADisplayLink

CADisplayLink默認(rèn)每秒運行60次,通過它的 frameInterval 屬性改變每秒運行幀數(shù),如設(shè)置為2,意味CADisplayLink每隔一幀運行一次,有效的邏輯每秒運行30次

屏幕刷新時調(diào)用:CADisplayLink是一個能讓我們以和屏幕刷新率同步的頻率將特定的內(nèi)容畫到屏幕上的定時器類。CADisplayLink以特定模式注冊到runloop后,每當(dāng)屏幕顯示內(nèi)容刷新結(jié)束的時候,runloop就會向CADisplayLink指定的target發(fā)送一次指定的selector消息, CADisplayLink類對應(yīng)的selector就會被調(diào)用一次。所以通常情況下,按照iOS設(shè)備屏幕的刷新率60次/秒

延遲:iOS設(shè)備的屏幕刷新頻率是固定的,CADisplayLink在正常情況下會在每次刷新結(jié)束都被調(diào)用,精確度相當(dāng)高。但如果調(diào)用的方法比較耗時,超過了屏幕刷新周期,就會導(dǎo)致跳過若干次回調(diào)調(diào)用機(jī)會。

如果CPU過于繁忙,無法保證屏幕60次/秒的刷新率,就會導(dǎo)致跳過若干次調(diào)用回調(diào)方法的機(jī)會,跳過次數(shù)取決CPU的忙碌程度。

使用場景:從原理上可以看出,CADisplayLink適合做界面的不停重繪,比如視頻播放的時候需要不停地獲取下一幀用于界面渲染。

+ (CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel

參數(shù):

  • target: 調(diào)用者

  • sel: 執(zhí)行的方法

示例:

- (void) displayLink {
 
 CADisplayLink *display = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayRun:)];
 
 // 大概1s執(zhí)行一次
// 取值范圍 1--100, 值越大, 頻率越高
 display.preferredFramesPerSecond = 2;
 
 [display addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)displayRun:(CADisplayLink *)link {
 
 static NSInteger num = 0;
 
 NSLog(@"%ld", (long)num);
 num++;
 
 if (num > 4) {
 
 [link invalidate];
 
 NSLog(@"end");
 }
}

這里的示例不太恰當(dāng), 不應(yīng)該在這種場合使用,

另外, 我們可以使用他的 paused 屬性, 來使其暫停, 或繼續(xù):

// 暫停
 display.paused = YES;
// 繼續(xù)
 display.paused = NO;

關(guān)于“iOS中如何使用各種定時器”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學(xué)到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

ios
AI