什么是RunLoop
簡單的來說就是運(yùn)行循環(huán)、跑圈。
- 保持著程序的持續(xù)運(yùn)行并接受用戶輸入
- 處理APP中的各種事件(觸摸,定時器,Selector)
- 節(jié)省了CPU資源,提高程序性能,即該做事時做事,該休息時休息。
RunLoop對象
iOS中有2套API來訪問RunLoop
- Foundation
NSRunloop - Core Foundation
CFRunLoopRef
CFRunLoopRef是用C語言寫的更底層一些。NSRunLoop其實(shí)是對CFRunLoopRef的一個簡單封裝,CFRunLoopRef的性能高一點(diǎn)
獲取RunLoop對象
- NSRunLoop
[NSRunLoop currentRunLoop];獲得當(dāng)前線程的RunLoop對象
[NSRunLoop mainRunLoop];獲得主線程的RunLoop對象
- CFRunLoopRef
CFRunLoopGetCurrent();//獲得當(dāng)前線程的RunLoop對象
CFRunLoopGetMain();//獲得主線程的RunLoop對象
RunLoop與線程間的關(guān)系
- 每條線程都有唯一的一個與之對應(yīng)的RunLoop對象
- 主線程的RunLoop已經(jīng)自動創(chuàng)建完成,子線程的需要手動創(chuàng)建
- RunLoop在第一次獲取時創(chuàng)建,在線程結(jié)束時銷毀
CFRunLoopRef

image.png
- CFRunLoopRefModeRef
代表RunLoop的運(yùn)行模式。
kCFRunLoopDefaultMode //app的默認(rèn)Mode,通常主線程是在這個Mode下運(yùn)行
UITrackingRunLoopMode //界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其他 Mode 影響
UIInitializationRunLoopMode //在剛啟動 App 時第進(jìn)入的第一個 Mode,啟動完成后就不再使用
GSEventReceiveRunLoopMode //接受系統(tǒng)事件的內(nèi)部 Mode,通常用不到
kCFRunLoopCommonModes //這是一個占位用的Mode,不是一種真正的Mode
- CFRunLoopObserverRef
觀察者,能夠監(jiān)聽RunLoop的狀態(tài)變化。
kCFRunLoopEntry //即將進(jìn)入loop
kCFRunLoopBeforeTimers //即將處理timer
kCFRunLoopBeforeSources //即將處理source
kCFRunLoopBeforeWaiting //即將進(jìn)入休眠
kCFRunLoopAfterWaiting //即將休眠中喚醒
kCFRunLoopExit //即將退出loop
kCFRunLoopAllActivities //所有狀態(tài)
- CFRunLoopSourceRef
RunLoop要處理的事件源,定義了兩種Source
Source0:處理APP內(nèi)部時間,APP自己負(fù)責(zé)管理(觸發(fā)),如UIevent...基于非Port的
Source1:由RunLoop和內(nèi)核管理的系統(tǒng)的一些事件,基于Port的。 - CFRunLoopTimerRef
是基于事件的觸發(fā)器,即NSTimer
創(chuàng)建一個RunLoop之后,有默認(rèn)的運(yùn)行模式mode,也可以為RunLoop指定運(yùn)行模式,RunLoop啟動必須得有運(yùn)行模式,而且在運(yùn)行模式中必須還有timer或是source事件其中之一,否則RunLoop就會退出。CFRunLoopModeRef代表RunLoop的運(yùn)行模式
一個 RunLoop 包含若干個Mode,每個Mode又包含若干個Source、Timer、Observer
每次RunLoop啟動時,只能指定其中一個Mode,這個Mode被稱作 CurrentMode
如果需要切換Mode,只能退出RunLoop,再重新指定一個Mode進(jìn)入,這樣做主要是為了分隔開不同組的Source、Timer、Observer,讓其互不影響。

image.png

image.png
/*
1參:怎么分配存儲空間
2參:眼監(jiān)聽的狀態(tài) kCFRunLoopAllActivities 所有狀態(tài)
3參:是否持續(xù)監(jiān)聽
4參:優(yōu)先級 總是傳0
5參:當(dāng)狀態(tài)改變時候的回調(diào)
*/
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
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(@"被喚醒");
case kCFRunLoopExit:
NSLog(@"runloop退出");
break;
default:
break;
}
});
/*
1參:要監(jiān)聽那個runloop
2參:觀察者
3參:運(yùn)行模式
*/
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
CFRelease(observer);//移除
NSRunLoop
- NSTimer問題
NSTimer使用scheduledTimerWithTimeInterval此方法創(chuàng)建的定時器默認(rèn)加到了NSRunLoop中,并且設(shè)置運(yùn)行模式為默認(rèn)default,但是如果有滾動事件的時候,定時器就會停止工作。
解決辦法:更改NSRunLoop的Mode為NSRunLoopCommonModes,滾動拖拽頁面時定時器仍然工作。
//1.創(chuàng)建定時器
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];
//添加至RunLoop并更改運(yùn)行模式
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
//開啟runloop
[currentRunloop run];
- 線程常駐
創(chuàng)建類GYThread繼承NSThread。GYThread.m中添加dealloc。
- (void)dealloc{
NSLog(@"線程被銷毀了");
}
初始化線程,并創(chuàng)建該線程的RunLoop,s
_thread = [[GYThread alloc]initWithTarget:self selector:@selector(threadtest) object:nil];
_thread.name = @"geyang";
[_thread start];
- (void)threadtest{
NSLog(@"--%@--", [NSThread currentThread]);
//啟動該?線程的runloop
[[NSRunLoop currentRunLoop]addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop]run];
}
//檢測線程是否被銷毀
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self performSelector:@selector(threadAgain) onThread:_thread withObject:nil waitUntilDone:NO];
}
- (void)threadAgain{
NSLog(@"---%@---", [NSThread currentThread]);
}
此時,我們可以點(diǎn)擊屏幕可看到打印結(jié)果
2017-07-13 15:23:24.382 RunLoop與線程[9290:1054512] ---<GYThread: 0x608000269300>{number = 3, name = geyang}---
說明該線程并沒有被銷毀