RunLoop編程官方文檔翻譯地址:http://m.itdecent.cn/p/4c38d16a29f1。
下面是里面提到的使用RunLoop的案例.官方提到下面的場(chǎng)景應(yīng)該考慮使用RunLoop.(具體見翻譯文檔的5.什么時(shí)候會(huì)用一個(gè)run loop)
繼承NSThread創(chuàng)建一個(gè)MyThread類,重寫dealloc方法,打印線程結(jié)束的信息。如果程序存在下面的代碼,startNewThread方法里面的代碼執(zhí)行完后就會(huì)提示線程退出了。沒有辦法控制線程的運(yùn)行時(shí)間。僅僅能開啟輔助線程執(zhí)行代碼,一般情況下這樣也足夠了,所以蘋果官方文檔說明run loop的開啟是運(yùn)用在需要和線程有更多交互的場(chǎng)合上的。
- (void)viewDidLoad {
[super viewDidLoad];
MyThread * thread = [[MyThread alloc]initWithTarget:self
selector:@selector(startNewThread) object:nil];
[thread start];
}
-(void)startNewThread{
NSLog(@"輔助線程執(zhí)行的代碼");
}
1. 使用端口或者自定義的輸入源和其他線程通信
這種類型的沒有遇到過,文檔后面有一些相應(yīng)地代碼,但是感覺比較復(fù)雜。不知道什么時(shí)候用這種場(chǎng)景,以后用到了補(bǔ)充。
2. 線程上使用定時(shí)器
一般情況下,自己使用定時(shí)器都是在主線程上,主線程的runloop默認(rèn)是開啟的。定時(shí)器提供了一個(gè)可以直接將定時(shí)器添加到當(dāng)前線程的run loop的NSDefaultRunLoopMode構(gòu)造方法,所以很多時(shí)候什么都不需要做,就能正常使用。但是如果在非主線程上添加定時(shí)器就需要手動(dòng)開啟run loop了。如果不開啟的話和上面的代碼沒有什么區(qū)別,線程執(zhí)行完startNewThread的方法后就退出了,會(huì)導(dǎo)致周期性執(zhí)行定時(shí)器任務(wù)根本實(shí)現(xiàn)不了。那如果我在startNewThread方法里面加上一個(gè)do {;} while(1);雖然雖然能防止線程退出,但是線程會(huì)進(jìn)入死循環(huán)同樣無法執(zhí)行定時(shí)器任務(wù)。所以run loop開啟的意義可以使得和線程有更多的交互,讓線程在忙碌的時(shí)候忙碌,不忙碌的時(shí)候休眠。對(duì)比主線程是一樣的,如果主線程有任務(wù)主線程會(huì)執(zhí)行任務(wù),沒有任務(wù)的話就不耗費(fèi)資源。但是并沒有退出(否則app就退出了);
-(void)startNewThread{
//獲取當(dāng)前線程的runloop對(duì)象
NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop];
//結(jié)構(gòu)體
CFRunLoopObserverContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};
//創(chuàng)建runloop觀察者,綁定run loop。
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
kCFRunLoopAllActivities, YES, 0,myRunLoopObserver, &context);
if (observer)
{
CFRunLoopRef cfLoop = [myRunLoop getCFRunLoop];//獲取Core Foundation形式的runloop引用
//為run loop的默認(rèn)的模式添加觀察者
CFRunLoopAddObserver(cfLoop, observer, kCFRunLoopDefaultMode);
}
// Create and schedule the timer.
[NSTimer scheduledTimerWithTimeInterval:1 target:self
selector:@selector(doFireTimer:) userInfo:nil repeats:YES];
// NSTimer * timer = [NSTimer timerWithTimeInterval:0.1 repeats:YES block:^(NSTimer * _Nonnull timer) {
// NSLog(@"純凈的定時(shí)器");
//}];
//[myRunLoop addTimer:timer forMode:NSDefaultRunLoopMode];
[myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
}

上面的代碼在輔助線程中,開啟了run loop。并為run loop添加了觀察者和定時(shí)器源,循環(huán)里面讓run loop運(yùn)行3秒鐘。
[myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];。RunLoop運(yùn)行3秒,然后線程退出,在這3秒期間,正確的在該線程上處理了定時(shí)器的事件,并且觀察者在回調(diào)函數(shù)中觀察到了run loop運(yùn)行過程需要通知給觀察者的狀態(tài)信息。該案例中如果沒有為run loop添加定時(shí)器和觀察者,線程會(huì)如文檔描述的那樣立刻退出。

中文翻譯可以看翻譯文檔的
4. run loop一些列的事件,fire這個(gè)單詞一直想不出好的中文詞對(duì)應(yīng)。下面的枚舉觀察者可以觀察到的run loop的狀態(tài)。
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0),//進(jìn)入runloop,對(duì)應(yīng)步驟1
kCFRunLoopBeforeTimers = (1UL << 1),//將要處理一個(gè)定時(shí)器,對(duì)應(yīng)步驟2
kCFRunLoopBeforeSources = (1UL << 2),//將要處理一個(gè)輸入源,對(duì)應(yīng)步驟3
kCFRunLoopBeforeWaiting = (1UL << 5),//將要進(jìn)入睡眠,對(duì)應(yīng)步驟6
kCFRunLoopAfterWaiting = (1UL << 6),//已經(jīng)喚醒,但是還沒有處理喚醒run loop的事件,對(duì)應(yīng)步驟8
kCFRunLoopExit = (1UL << 7),//將要退出,對(duì)應(yīng)步驟10
kCFRunLoopAllActivities = 0x0FFFFFFFU //表示監(jiān)控所有的活動(dòng)
};
3. 在cocoa應(yīng)用中使用任意一個(gè)performSelector…方法
比較常見的是performSelectorOnMainThread,非主線程中不能更新UI,開啟額外線程進(jìn)行網(wǎng)絡(luò)請(qǐng)求后有時(shí)可以調(diào)用這個(gè)方法在主線程中更新UI。因?yàn)橹骶€程中runloop已經(jīng)開啟了,所以很自然就成功了。但是如果在非主線程執(zhí)行某個(gè)方法。就需要開啟線程的runloop了。系統(tǒng)提供了一系列類似的方法。
performSelectorOnMainThread:withObject:waitUntilDone:
performSelectorOnMainThread:withObject:waitUntilDone:modes:
執(zhí)行特定的selector在主線程的下一個(gè)run loop回路。這兩個(gè)方法給你提供了選項(xiàng)來阻斷當(dāng)前線程直到selector被執(zhí)行完畢。
performSelector:onThread:withObject:waitUntilDone:
performSelector:onThread:withObject:waitUntilDone:modes:
執(zhí)行特定的selector在任意線程上,這些線程通過NSThread對(duì)象表示。同樣提供了阻斷當(dāng)前線程直到selector被執(zhí)行。
performSelector:withObject:afterDelay:
performSelector:withObject:afterDelay:inModes:
在當(dāng)前線程上下一個(gè)run loop回路中執(zhí)行selector,并附加了延遲選項(xiàng)。因?yàn)樗却乱粋€(gè)run loop回路到來才執(zhí)行selector,這些方法從當(dāng)前執(zhí)行的代碼中提供了一個(gè)自動(dòng)的微小延遲。多個(gè)排隊(duì)的selector會(huì)按照順序一個(gè)一個(gè)的執(zhí)行。
cancelPreviousPerformRequestsWithTarget:
cancelPreviousPerformRequestsWithTarget:selector:object:
讓你取消一個(gè)通過performSelector:withObject:afterDelay: or performSelector:withObject:afterDelay:inModes: method方法發(fā)送到當(dāng)前線程的消息。
*** 這段代碼讓圖片在默認(rèn)的模式下加載,如果用戶在進(jìn)行滑動(dòng)操作,不會(huì)進(jìn)入這個(gè)模式,可以解決部分滑動(dòng)卡頓問題 ***
[self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"placeholder"] afterDelay:3.0 inModes:@[NSDefaultRunLoopMode]];
4. 使得線程不被殺死去做周期性任務(wù)
可以根據(jù)業(yè)務(wù)需求,設(shè)置runloop運(yùn)行時(shí)間??梢灶愃朴谏厦娴陌咐V芷谛缘脑谛戮€程上執(zhí)行runmethod方法。
下面對(duì)應(yīng)了3和4兩種情況.
- (void)viewDidLoad {
[super viewDidLoad];
MyThread * thread = [[MyThread alloc]initWithTarget:self selector:@selector(longrun) object:nil];
[thread start];
[NSTimer scheduledTimerWithTimeInterval:0.5 repeats:YES block:^(NSTimer * _Nonnull timer) {
[self performSelector:@selector(runmethod) onThread:thread withObject:nil waitUntilDone:YES modes:@[NSDefaultRunLoopMode]];
}];
}
-(void)longrun{
NSRunLoop * runLoop = [NSRunLoop currentRunLoop];
//為了防止runloop退出,添加一個(gè)端口。
[runLoop addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];
[runLoop runUntilDate:[NSDate distantFuture]];
}
-(void)runmethod
{
NSLog(@"%@ 輔助線程上執(zhí)行的代碼",[NSThread currentThread]);
}
如果需要某個(gè)線程一直執(zhí)行,等待某個(gè)條件具備時(shí)觸發(fā)可以參考蘋果提供的案例
- (void)skeletonThreadMain
{
// Set up an autorelease pool here if not using garbage collection.
BOOL done = NO;
// Add your sources or timers to the run loop and do any other setup.
do
{
// Start the run loop but return after each source is handled.
SInt32 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, YES);
// If a source explicitly stopped the run loop, or if there are no
// sources or timers, go ahead and exit.
if ((result == kCFRunLoopRunStopped) || (result == kCFRunLoopRunFinished))
done = YES;
// Check for any other exit conditions here and set the
// done variable as needed.
}
while (!done);
// Clean up code here. Be sure to release any allocated autorelease pools.
}
CFRunLoopRunInMode在這里表示在kCFRunLoopDefaultMode模式下啟動(dòng),時(shí)間長(zhǎng)度為10秒,如果源的事件被處理runloop應(yīng)該退出,而不是非要等到10秒耗盡。返回結(jié)果如下
- 時(shí)間耗盡(kCFRunLoopRunTimedOut)
- 被
CFRunLoopStop函數(shù)調(diào)用導(dǎo)致runloop(kCFRunLoopRunStopped) - 源事件被處理了(kCFRunLoopRunHandledSource)
- 沒有了任何源或定時(shí)器(kCFRunLoopRunFinished)
代碼設(shè)定了一個(gè)標(biāo)志位。并且開啟runloop事件為10秒,當(dāng)然也可以設(shè)定為其他的值。在每個(gè)源得到處理后返回結(jié)果,根據(jù)結(jié)果,狀態(tài)設(shè)定標(biāo)志位狀態(tài)。最后可以進(jìn)行一些線程退出前的操作。在這里可以依然像上面那些方法一樣為runloop添加定時(shí)器和其他輸入源和觀察者。