1.1RunLoop是什么
從字面意思看就是運(yùn)行循環(huán),跑圈次慢。
其實(shí)它內(nèi)部就是一個(gè)do-while循環(huán),在這個(gè)循環(huán)內(nèi)部不斷的處理各種事件
一個(gè)線程對(duì)應(yīng)一個(gè)Runloop,主線程的RunLoop默認(rèn)已經(jīng)啟動(dòng),子線程的Runloop需要自己?jiǎn)?dòng)
RunLoop只能選擇一個(gè)Mode 啟動(dòng),如果當(dāng)前Mode沒(méi)有任何Source,Timer,observer髓涯,那么就是直接退出RunLoop
1.2RunLoop主要作用
1.保持程序的持續(xù)運(yùn)行
2.處理App中的各種事件(比如觸摸時(shí)間、定時(shí)器實(shí)踐哈扮、selector事件)
3.節(jié)省CPU資源纬纪,提高程序性能;該做事時(shí)做事,該休息時(shí)休息滑肉。
main函數(shù)中的RunLoop
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
1.在UIApplicationMain函數(shù)內(nèi)部就啟動(dòng)了一個(gè)RunLoop
2.所以UIApplicationMain函數(shù)一直沒(méi)有返回包各,保持了程序的持續(xù)運(yùn)行。
3.這個(gè)默認(rèn)啟動(dòng)的Runloop是跟主線程相關(guān)聯(lián)的靶庙。
1.3 RunLoop對(duì)象
iOS中有2套API來(lái)訪問(wèn)和使用RunLoop
1.Foundation 中 NSRunLoop
2.Core Foundation 中 CFRunLoopRef
NSRunLoop 和 CFRunLoopRef都代表著RunLoop對(duì)象髓棋。
NSRunLoop 是基于CFRunLoopRef的一層OC包裝,所以要更深層次了解RunLoop內(nèi)部結(jié)構(gòu)惶洲,還是得使用CFRunLoopRef對(duì)象。
獲得RunLoop對(duì)象
//獲取當(dāng)前RunLoop
[NSRunLoop currentRunLoop];
CFRunLoopGetCurrent();
//獲取主線程RunLoop
[NSRunLoop mainRunLoop];
CFRunLoopGetMain();
1.4 RunLoop與線程
1.每條線程都有唯一的一個(gè)與之對(duì)應(yīng)的RunLoop對(duì)象膳犹。
2.主線程的RunLoop已經(jīng)自動(dòng)創(chuàng)建好了恬吕,子線程的RunLoop需要主動(dòng)創(chuàng)建
3.RunLoop在第一次獲取時(shí)創(chuàng)建,在線程結(jié)束時(shí)銷(xiāo)毀须床。
注意:
在子線程中铐料,RunLoop對(duì)象沒(méi)有創(chuàng)建,并且不能通過(guò)alloc創(chuàng)建,RunLoop對(duì)象是懶加載钠惩,通過(guò)調(diào)用currentRunLoop來(lái)創(chuàng)建柒凉。
1.5 RunLoop模式
CFRunLoopModeRef代表RunLoop的運(yùn)行模式
1.一個(gè)RunLoop包含若干個(gè)Mode,每個(gè)Mode又包含若干個(gè)Source/Timer/Observer
2.每次RunLoop啟動(dòng)時(shí),只能指定其中一個(gè)Mode,這個(gè)Mode被稱作CurrentMode
3.如果需要切換Mode,只能退出Loop.再重新制定一個(gè)Mode進(jìn)入篓跛,這樣做主要目的是為了分隔開(kāi)不同組的Source/Timner/Observer膝捞,讓其互不影響。
kCFRunLoopDefaultMode:app的默認(rèn)mode,通常主線程就是在這個(gè)Mode下運(yùn)行
UITrackRunLoopMode:界面跟蹤Mode,用scrollView追蹤觸摸滑動(dòng)愧沟,保證界面滑動(dòng)時(shí)不受其他Mode影響蔬咬。
NSRunLoopCommonModes:可以同時(shí)運(yùn)行在kCFRunLoopDefaultMode和UITrackRunLoopMode模式下。
RunLoop組成
當(dāng)RunLoop進(jìn)入某一個(gè)模式的時(shí)候沐寺,需要處理3大塊的內(nèi)容林艘,timer,source,observe。
2.1 RunLoop - timer
Timer 就是平常用的定時(shí)器 NSTimer
創(chuàng)建定時(shí)器
方法1: 使用scheduledTimerWithTimeInterval 函數(shù)
[NSTimer scheduledTimerWithTimeInterval:2.0f target:self selector:@selector(run) userInfo:nil repeats:NO];
調(diào)用了scheduledTimerWithTimeInterval返回的定時(shí)器混坞,已經(jīng)被添加到runloop中了NSDefaultRunLoopMode
如果想修改模式狐援,比如想在滑動(dòng)的時(shí)候也調(diào)用定時(shí)器
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0f target:self selector:@selector(run) userInfo:nil repeats:NO];
//修改模式
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
方法2: 使用timerWithTimeInterval
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
//定時(shí)器只運(yùn)行在NSDefaultRunLoopMode下,一旦runloop進(jìn)入其他模式究孕,這個(gè)定時(shí)器就不會(huì)工作
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
2.2 RunLoop - source
source啥酱,事件源,輸入源蚊俺。比如一些觸摸事件等懈涛。一般由系統(tǒng)決定。
實(shí)踐分類:
source0 : 非基于Port的泳猬。不是其他線程批钠,內(nèi)核發(fā)布消息。
source1: 基于Port的得封,通過(guò)內(nèi)核和其他線程通信埋心,接收,分發(fā)系統(tǒng)事件忙上。
2.3 RunLoop - observe
observe 用來(lái)監(jiān)聽(tīng)RunLoop的狀態(tài)拷呆。
監(jiān)聽(tīng)的時(shí)間點(diǎn)有以下幾個(gè):
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), //即將進(jìn)入runloop 1
kCFRunLoopBeforeTimers = (1UL << 1), //即將處理timer 2
kCFRunLoopBeforeSources = (1UL << 2),//即將處理source 4
kCFRunLoopBeforeWaiting = (1UL << 5),//即將進(jìn)入休眠 32
kCFRunLoopAfterWaiting = (1UL << 6),//剛從休眠中醒來(lái) 64
kCFRunLoopExit = (1UL << 7),//即將退出runloop 128
kCFRunLoopAllActivities = 0x0FFFFFFFU //上面所有狀態(tài)
};
給當(dāng)前的runloop添加一個(gè)觀察者,攔截一些事件
//創(chuàng)建一個(gè)observe
CFRunLoopObserverRef observe = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
NSLog(@"%zd",activity);
});
//添加觀察者:監(jiān)聽(tīng)RunLoop的狀態(tài)
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observe, kCFRunLoopDefaultMode);
//釋放observe
CFRelease(observe);
補(bǔ)充:CF框架的內(nèi)存管理
/**
*CF的內(nèi)存管理(core Foundation)
1.凡是帶有create,copy,retain等字眼的函數(shù)疫粥,創(chuàng)建出來(lái)的對(duì)象茬斧,都需要在最后做一次release
*比如CFRunLoopObserveCreate
2.release函數(shù)
CFRelease(對(duì)象);
*/
3. RunLoop整體邏輯
其實(shí)在進(jìn)入runloop之前,會(huì)進(jìn)行一個(gè)非空判斷梗逮,判斷下modes是否為空项秉,如果為空,就會(huì)直接退出runloop慷彤。
4. RunLoop實(shí)踐
4.1 ImageView顯示
scrollview滾動(dòng)的時(shí)候加載圖片出現(xiàn)卡頓的情況,需要延遲顯示娄蔼,可以利用runloop進(jìn)行延遲加載
[imageview performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"1"] afterDelay:0.3 inModes:@[NSDefaultRunLoopMode]];
4.2 常駐線程
常駐線程:希望線程一直永遠(yuǎn)不死怖喻,一直在后臺(tái)運(yùn)行,避免多次創(chuàng)建線程銷(xiāo)毀。比如:在子線程中開(kāi)啟一個(gè)定時(shí)器岁诉,在子線程中進(jìn)行一些長(zhǎng)期監(jiān)控锚沸。
@interface ViewController ()
@property (nonatomic, strong) NSThread *thread;
@end
@implementation ViewController
步驟1: 創(chuàng)建子線程
- (void)viewDidLoad {
[super viewDidLoad];
//創(chuàng)建一個(gè)線程并且啟動(dòng)
self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[self.thread start];
}
步驟2: 創(chuàng)建runloop
- (void)run
{
//在創(chuàng)建了線程以后創(chuàng)建RunLoop。
//原因:開(kāi)啟子線程后涕癣,執(zhí)行完任務(wù)哗蜈,子線程就會(huì)死亡,通過(guò)創(chuàng)建runloop可以使子線程常駐属划。就像主線程一樣恬叹。
//runloop中Mode如果沒(méi)有source,observe,timer,runloop就會(huì)退出。首先先創(chuàng)建一個(gè)port(相當(dāng)于source),第二步運(yùn)行runloop同眯。
[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}
步驟3: 測(cè)試常駐線程
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
}
- (void)test
{
NSLog(@"%@----thread",[NSThread currentThread]);
NSLog(@"test");
}