看了很多關(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的常見用例
- 在子線程中執(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í)行
- 線程執(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];
});
- 圖片加載時(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]];
- 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地址