這篇文章將為大家詳細(xì)講解有關(guān)iOS中如何使用各種定時(shí)器,小編覺得挺實(shí)用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)公司!專注于網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、成都微信小程序、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了武寧免費(fèi)建站歡迎大家使用!一. NSTimer
NSTimer的初始化方法有以下幾種:
會自動啟動, 并加入 MainRunloop 的 NSDefaultRunLoopMode 中,
注意:這里的自動啟動, 并不是馬上就會啟動, 而是會延遲大概一個interval的時(shí)間:
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block
參數(shù):
internal : 時(shí)間間隔, 多久調(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");
這時(shí), 控制臺的輸出:
2016-12-29 16:29:53.901 定時(shí)器[11673:278678] start 2016-12-29 16:29:54.919 定時(shí)器[11673:278678] 0 2016-12-29 16:29:55.965 定時(shí)器[11673:278678] 1 2016-12-29 16:29:56.901 定時(shí)器[11673:278678] 2 2016-12-29 16:29:57.974 定時(shí)器[11673:278678] 3 2016-12-29 16:29:58.958 定時(shí)器[11673:278678] 4 2016-12-29 16:29:58.959 定時(shí)器[11673:278678] end
可以看出, 這里的internal設(shè)置為1s, 大概延遲了1s才開始執(zhí)行block里的內(nèi)容;
這里的停止定時(shí)器, 我直接在block里進(jìn)行的, 如果使用一個全局變量來再其他地方手動停止定時(shí)器,需要這樣進(jìn)行:
[self.timer invalidate]; self.timer = nil;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo
參數(shù):
ti: 重復(fù)執(zhí)行時(shí)間間隔
invocation: NSInvocation實(shí)例, 其用法見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 定時(shí)器[12089:289673] 0---<__NSCFTimer: 0x60000017d940> 2016-12-29 16:52:54.029 定時(shí)器[12089:289673] start 2016-12-29 16:52:55.104 定時(shí)器[12089:289673] 1---<__NSCFTimer: 0x60000017d940> 2016-12-29 16:52:56.095 定時(shí)器[12089:289673] 2---<__NSCFTimer: 0x60000017d940> 2016-12-29 16:52:57.098 定時(shí)器[12089:289673] 3---<__NSCFTimer: 0x60000017d940> 2016-12-29 16:52:58.094 定時(shí)器[12089:289673] 4---<__NSCFTimer: 0x60000017d940>
可以看出, 這里定時(shí)器是立馬就執(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 定時(shí)器[12183:292324] 0---<__NSCFTimer: 0x60000016dbc0>--第二個參數(shù)是字符串 2016-12-29 16:57:45.088 定時(shí)器[12183:292324] start 2016-12-29 16:57:46.161 定時(shí)器[12183:292324] 1---<__NSCFTimer: 0x60000016dbc0>--第二個參數(shù)是字符串 2016-12-29 16:57:47.161 定時(shí)器[12183:292324] 2---<__NSCFTimer: 0x60000016dbc0>--第二個參數(shù)是字符串 2016-12-29 16:57:48.150 定時(shí)器[12183:292324] 3---<__NSCFTimer: 0x60000016dbc0>--第二個參數(shù)是字符串 2016-12-29 16:57:49.159 定時(shí)器[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: 時(shí)間間隔
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 定時(shí)器[12328:296879] start 2016-12-29 17:05:12.655 定時(shí)器[12328:296879] 0---<__NSCFTimer: 0x608000162700>--這是攜帶的參數(shù) 2016-12-29 17:05:13.661 定時(shí)器[12328:296879] 1---<__NSCFTimer: 0x608000162700>--這是攜帶的參數(shù) 2016-12-29 17:05:14.664 定時(shí)器[12328:296879] 2---<__NSCFTimer: 0x608000162700>--這是攜帶的參數(shù) 2016-12-29 17:05:15.651 定時(shí)器[12328:296879] 3---<__NSCFTimer: 0x608000162700>--這是攜帶的參數(shù) 2016-12-29 17:05:16.650 定時(shí)器[12328:296879] 4---<__NSCFTimer: 0x608000162700>--這是攜帶的參數(shù)
下面這三種方式創(chuàng)建定時(shí)器的用法, 和上面相應(yīng)的方法類似, 需要注意的是, 這樣創(chuàng)建的定時(shí)器, 并不會執(zhí)行, 需要我們手動來開啟定時(shí)器;
+ (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)前定時(shí)器添加到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 定時(shí)器[12498:301751] start 2016-12-29 17:12:15.013 定時(shí)器[12498:301751] 0 2016-12-29 17:12:16.018 定時(shí)器[12498:301751] 1 2016-12-29 17:12:17.011 定時(shí)器[12498:301751] 2 2016-12-29 17:12:18.024 定時(shí)器[12498:301751] 3 2016-12-29 17:12:19.023 定時(shí)器[12498:301751] 4 2016-12-29 17:12:19.023 定時(shí)器[12498:301751] end
定時(shí)器基本的創(chuàng)建方式就這些了, 還可以設(shè)置其他的屬性, 例如開啟時(shí)間, 這些直接參考其API 進(jìn)行設(shè)置即可;
注意:以上實(shí)例中, 我沒有使用全局的NSTimer 對象, 如果設(shè)置全局變量, 或者設(shè)置為屬性, 在停止定時(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í)行的定時(shí)器
void dispatch_source_set_timer(dispatch_source_t source, dispatch_time_t start, uint64_t interval, uint64_t leeway)
參數(shù):
source: 定時(shí)器
start: 開始時(shí)間, 當(dāng)我們使用 dispatch_time 或者 DISPATCH_TIME_NOW 時(shí),系統(tǒng)會使用默認(rèn)時(shí)鐘來進(jìn)行計(jì)時(shí)。然而當(dāng)系統(tǒng)休眠的時(shí)候,默認(rèn)時(shí)鐘是不走的,也就會導(dǎo)致計(jì)時(shí)器停止。使用 dispatch_walltime 可以讓計(jì)時(shí)器按照真實(shí)時(shí)間間隔進(jìn)行計(jì)時(shí);
interval: 間隔(如果設(shè)置為 DISPATCH_TIME_FOREVER 則只執(zhí)行一次)
leeway: 允許的誤差范圍; 計(jì)時(shí)不可能是百分百精確的, 即使設(shè)置為0, 也不是百分百精確的, 所以可以設(shè)置合理的允許誤差, 單位: 納秒(NSEC_PER_SEC)
相關(guān)內(nèi)容, 可參考文章: Dispatch Source Timer 的使用以及注意事項(xiàng)
// 重復(fù)執(zhí)行的定時(shí)器 - (void)gcdTimer1 { // 獲取全局隊(duì)列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 創(chuàng)建定時(shí)器 dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); // 開始時(shí)間 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è)置定時(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)閉定時(shí)器 dispatch_source_cancel(_timer); } }); // 開啟定時(shí)器 dispatch_resume(_timer); NSLog(@"start"); }
輸出:
2016-12-30 10:15:01.114 定時(shí)器[3393:99474] start 2016-12-30 10:15:02.187 定時(shí)器[3393:99796] 0 2016-12-30 10:15:03.114 定時(shí)器[3393:99796] 1 2016-12-30 10:15:04.186 定時(shí)器[3393:99796] 2 2016-12-30 10:15:05.188 定時(shí)器[3393:99796] 3 2016-12-30 10:15:06.188 定時(shí)器[3393:99796] 4 2016-12-30 10:15:06.188 定時(shí)器[3393:99796] end
這里的開始時(shí)間設(shè)置了1s的間隔, 所以1s之后才開始執(zhí)行,可以設(shè)置使用DISPATCH_TIME_NOW來立馬執(zhí)行;
注意:
這里的開始時(shí)間(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)休眠時(shí)還會繼續(xù)計(jì)時(shí), 而后者在系統(tǒng)休眠時(shí)就停止計(jì)時(shí), 待系統(tǒng)重新激活時(shí), 接著繼續(xù)計(jì)時(shí);
停止計(jì)時(shí)器:
停止GCD定時(shí)器的方式, Dispatch Source Timer 的使用以及注意事項(xiàng)中有提及, 主要有以下兩種:
// 關(guān)閉定時(shí)器 // 完全銷毀定時(shí)器, 重新開啟的話需要重新創(chuàng)建 // 全局變量, 關(guān)閉后需要置為nil dispatch_source_cancel(_timer); // 暫停定時(shí)器 // 可使用dispatch_resume(_timer)再次開啟 // 全局變量, 暫停后不能置為nil, 否則不能重新開啟 dispatch_suspend(_timer);
三. CADisplayLink
CADisplayLink默認(rèn)每秒運(yùn)行60次,通過它的 frameInterval 屬性改變每秒運(yùn)行幀數(shù),如設(shè)置為2,意味CADisplayLink每隔一幀運(yùn)行一次,有效的邏輯每秒運(yùn)行30次
屏幕刷新時(shí)調(diào)用:CADisplayLink是一個能讓我們以和屏幕刷新率同步的頻率將特定的內(nèi)容畫到屏幕上的定時(shí)器類。CADisplayLink以特定模式注冊到runloop后,每當(dāng)屏幕顯示內(nèi)容刷新結(jié)束的時(shí)候,runloop就會向CADisplayLink指定的target發(fā)送一次指定的selector消息, CADisplayLink類對應(yīng)的selector就會被調(diào)用一次。所以通常情況下,按照iOS設(shè)備屏幕的刷新率60次/秒
延遲:iOS設(shè)備的屏幕刷新頻率是固定的,CADisplayLink在正常情況下會在每次刷新結(jié)束都被調(diào)用,精確度相當(dāng)高。但如果調(diào)用的方法比較耗時(shí),超過了屏幕刷新周期,就會導(dǎo)致跳過若干次回調(diào)調(diào)用機(jī)會。
如果CPU過于繁忙,無法保證屏幕60次/秒的刷新率,就會導(dǎo)致跳過若干次調(diào)用回調(diào)方法的機(jī)會,跳過次數(shù)取決CPU的忙碌程度。
使用場景:從原理上可以看出,CADisplayLink適合做界面的不停重繪,比如視頻播放的時(shí)候需要不停地獲取下一幀用于界面渲染。
+ (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中如何使用各種定時(shí)器”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學(xué)到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)建站www.cdcxhl.com,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。