談?wù)凴unLoop

看了很多關(guān)于RunLoop的文章,還是對(duì)RunLoop的底層原理不能理解太多,可能是技術(shù)沒達(dá)到水準(zhǔn)的原因吧。如此,做個(gè)隨筆記錄下,等以后若是能懂一二的話再來修改。

一、 什么是RunLoop

官方對(duì)RunLoop的定義:RunLoop系統(tǒng)中和線程相關(guān)的基礎(chǔ)架構(gòu)的組成部分(和線程相關(guān)),一個(gè)RunLoop是一個(gè)事件處理環(huán),系統(tǒng)利用這個(gè)事件處理環(huán)來安排事務(wù),協(xié)調(diào)輸入的各種事件。

曾網(wǎng)上看到過關(guān)于RunLoop的場(chǎng)景形容:我們把線程比作一輛跑車,把這輛跑車的主人比作RunLoop,那么在沒有'主人'的時(shí)候,這個(gè)跑車的生命是直線型的,其啟動(dòng),運(yùn)行完之后就會(huì)廢棄(沒有人對(duì)其進(jìn)行控制,'撞壞'被收回),當(dāng)有了RunLoop這個(gè)主人之后,‘線程’這輛跑車的生命就有了保障,這個(gè)時(shí)候,跑車的生命是環(huán)形的,并且在主人有比賽任務(wù)的時(shí)候就會(huì)被RunLoop這個(gè)主人所喚醒,在沒有任務(wù)的時(shí)候可以休眠,這樣可以增加跑車的效率,也就是說RunLoop是為線程所服務(wù)的。

  • 一條線程對(duì)應(yīng)一個(gè)RunLoop,主線程的RunLoop默認(rèn)已經(jīng)創(chuàng)建好了, 而子線程需要我們自己手動(dòng)創(chuàng)建。
  • 一個(gè)runLoop包含若干個(gè)Model,每個(gè)Mode又包含若干個(gè)Source/Timer/Observer,每次RunLoop啟動(dòng)時(shí),只能指定其中一個(gè)Mode,這個(gè)Mode被稱作 CurrentMode。
  • 如果需要切換Mode,只能退出Loop,再重新指定一個(gè)Mode進(jìn)入。
  • 獲取主線程對(duì)應(yīng)的RunLoop對(duì)象 mainRunLoop/CFRunLoopGetMain
  • 獲取當(dāng)前線程對(duì)應(yīng)的RunLoop對(duì)象 currentRunLoop/CFRunLoopGetCurrent
  • NSDefaultRunLoopMode:App的默認(rèn)Mode,通常主線程是在這個(gè)Mode下運(yùn)行
  • UITrackingRunLoopMode:界面跟蹤Mode,用于ScrollView 追蹤觸摸滑動(dòng),保證界面滑動(dòng)時(shí)不受其他 Mode 影響
  • UIInitializationRunLoopMode: 在剛啟動(dòng)App時(shí)第進(jìn)入的第一個(gè) Mode,啟動(dòng)完成后就不再使用
  • GSEventReceiveRunLoopMode: 接受系統(tǒng)事件的內(nèi)部 Mode,通常用不到
  • NSRunLoopCommonModes: 這是一個(gè)偽模式,為一組runloop mode的集合,將timer加入此模式意味著在Common Modes中包含的所有模式下都可以處理。

二、 RunLoop的常見用例

  1. 在子線程中執(zhí)行perform selector
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self performSelector:@selector(method) onThread:[NSThread  currentThread]  withObject:nil waitUntilDone:NO];
         [[NSRunLoop currentRunLoop] run];
});

當(dāng)perform selector在后臺(tái)線程中執(zhí)行的時(shí)候,這個(gè)線程必須有一個(gè)開啟的runLoop,否則不會(huì)執(zhí)行

  1. 線程執(zhí)行完任務(wù)會(huì)死亡,runLoop為線程注入“活力”
- (void)test{
        NSThread *thread = [[NSThread alloc] initWithTarget:self  selector:@selector(threadRun) object:nil];
       [thread start];
       [self performSelector:@selector(method) onThread: thread withObject:nil waitUntilDone:NO];
}
- (void)threadRun{
     NSLog(@"---%s--",__func__);
}

performSelector 不會(huì)執(zhí)行,盡管thread是存在的,但這個(gè)線程在執(zhí)行threadRun任務(wù)之后已經(jīng)死亡了,也就不會(huì)再執(zhí)行后面的performSelector,正常情況下,后臺(tái)線程執(zhí)行完任務(wù)之后就處于死亡狀態(tài),避免這種情況的發(fā)生可以利用RunLoop,并且給它一個(gè)Source這樣來保證線程依舊還在

- (void)threadRun{
NSLog(@"---%s--",__func__);
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];
[runLoop run];
}

3.在子線程添加timer

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.5 repeats:TRUE block:^(NSTimer * _Nonnull timer) {
NSLog(@"action");
}];
[timer fire];
[[NSRunLoop currentRunLoop] run];
});
  1. 圖片加載時(shí)機(jī)
// 只在NSDefaultRunLoopMode模式下顯示圖片(為了使?jié)L動(dòng)更加流暢,scroll滾動(dòng)的時(shí)候不顯示圖片,尤其是當(dāng)圖片很大的時(shí)候尤其有意義)

[self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"appointment_duty_img"] afterDelay:1.0 inModes:@[NSDefaultRunLoopMode]];
  1. tableView或者collectionView里面每個(gè)cell需要顯示幾張圖片,而且這些圖片都是很大的圖
    定義RunLoop監(jiān)聽,監(jiān)聽回調(diào)方法,每次執(zhí)行一次繪制任務(wù),執(zhí)行完就把任務(wù)刪除
typedef void(^RunloopBlock)(void);

@property (nonatomic, strong) NSMutableArray *tasks;

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
***code
[self trySetImage]
***code
}

- (void)trySetImage{
__weak typeof(self) weakSelf = self;
[self addTask:^{
   [weakSelf setImage1];
}];
[self addTask:^{
   [weakSelf setImage2];
}];

[self addTask:^{
    [weakSelf setImage3];
}];
}

- (void)addTask:(RunloopBlock)task{
     [_tasks addObject:task];
}

- (void)addRunloopObserver{
CFRunLoopObserverContext context = {
0,
(__bridge void *)self,
&CFRetain,
&CFRelease,
NULL
};
static CFRunLoopObserverRef observer;
observer = CFRunLoopObserverCreate(NULL, kCFRunLoopAllActivities, YES, 0, &ObserverCallback, &context);
CFRunLoopAddObserver( CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
CFRelease(observer);
}
static void ObserverCallback (CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
/*
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"即將進(jìn)入runloop");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"即將處理timer");
break;
case kCFRunLoopBeforeSources:
NSLog(@"即將處理source");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"即將進(jìn)入睡眠");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"剛從睡眠中喚醒");
break;
case kCFRunLoopExit:
NSLog(@"即將退出");
break;
default:
break;
}
*/
ViewController *vc  = (__bridge ViewController *)info;
if (!vc.tasks.count) return;
RunloopBlock task  = [vc.tasks firstObject];
task();
[vc.tasks removeObjectAtIndex:0];
}

特別說明:本隨筆大部分借鑒了一些網(wǎng)上的文章,只作記錄和學(xué)習(xí),不作其他用途。
demo地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、RunLoop定義 從字面上看,run是運(yùn)行,執(zhí)行的意思,loop是循環(huán)的意思,其實(shí)RunLoop就是運(yùn)行循環(huán)...
    conowen閱讀 517評(píng)論 0 0
  • 來源:擊水湘江 鏈接:http://www.itdecent.cn/p/536184bfd163 實(shí)例化講解Run...
    __Lex閱讀 290評(píng)論 0 2
  • 轉(zhuǎn)自http://blog.ibireme.com/2015/05/18/runloop 深入理解RunLoop ...
    飄金閱讀 1,065評(píng)論 0 4
  • 轉(zhuǎn)載:http://www.cocoachina.com/ios/20150601/11970.html RunL...
    Gatling閱讀 1,547評(píng)論 0 13
  • 今天狀態(tài)不是很好。 可能是昨晚沒睡好。也可能是姨媽快到來了。 早上灌了一杯咖啡。沒啥緩解。蒙蒙的開始一天的論文。 ...
    Sanity娜娜閱讀 193評(píng)論 0 0

友情鏈接更多精彩內(nèi)容