1.IOS的Run Loops機(jī)制
Run Loops是線程的基礎(chǔ)部份纵柿,任何線程却汉,包括主結(jié)程廊酣,都包含了一個(gè)run loop對(duì)象锉罐,Cocoa和CoreFoundation層都有對(duì)應(yīng)的Run Loop實(shí)現(xiàn)帆竹。
Run loop 對(duì)線程的作用,就是用來(lái)控制當(dāng)有事件需要處理的時(shí)候脓规,讓線程快速響應(yīng)栽连,而當(dāng)沒(méi)有工作的時(shí)候,線程改為休息抖拦。
本質(zhì)上Run Loop是一個(gè)While死循環(huán)升酣。不停地監(jiān)聽事件以及處理事件。我們可以自己寫一個(gè)While循環(huán)來(lái)做到這點(diǎn)态罪,但是蘋果的封裝顯然會(huì)更好噩茄。比如可以有不同的運(yùn)行模式、不同的接收源和定時(shí)源复颈,不工作的時(shí)候休息等绩聘。
在一個(gè)應(yīng)用的主函數(shù)中:
int main(int argc, char * argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
UIApplicationMain()函數(shù)就會(huì)啟動(dòng)一個(gè)主線程,并且自動(dòng)為它設(shè)置一個(gè)Run Loop對(duì)象耗啦,但對(duì)除此以外凿菩,其它的線程需要明確去設(shè)置和啟動(dòng)。
2.適用場(chǎng)景
只有創(chuàng)建一個(gè)附屬線程的時(shí)候帜讲,才需要明確去運(yùn)行一個(gè)Run Loop.而且是在確定需要的時(shí)候才去設(shè)置和啟動(dòng)它衅谷,否則就必要了。例如似将,開一個(gè)線程去執(zhí)行一個(gè)明確的長(zhǎng)時(shí)間的任務(wù)获黔,就沒(méi)有必要蚀苛。起用Run Loop主要還是為了跟創(chuàng)建的線程可以有更多的交互。
而使用Run Loop一個(gè)明顯的好處就是:節(jié)約計(jì)算資源玷氏,同時(shí)也就節(jié)約用電了堵未。因?yàn)樵跊](méi)有觸發(fā)的時(shí)候線程是處于休眠狀態(tài)的,不會(huì)消耗CPU資源盏触。
3.結(jié)構(gòu)
一個(gè)Run Loop可以接收的事件類型有兩種:一種是輸入(Input Source);一種是時(shí)間資源(time source).前者異步傳遞事件的渗蟹,通常消息是來(lái)自其它線程或應(yīng)用發(fā)送的;而后者是同步事件的赞辩,比如:定時(shí)計(jì)劃雌芽,或者定時(shí)重復(fù)的工作。
4.觀察者
Run Loop對(duì)象的循環(huán)過(guò)程中可以添加觀察者對(duì)象辨嗽。整個(gè)Run Loop 在運(yùn)行過(guò)程中發(fā)生的事件具體如下:
- 通知觀察者Run Loop開始了膘怕;
- 通知觀察者任何預(yù)設(shè)好的時(shí)間已經(jīng)觸發(fā);
- 通知觀察者任何接入端口的輸入源準(zhǔn)備被觸發(fā)召庞;
- 觸發(fā)任何非端口方式接入的輸入源岛心;
- 如果有任何一個(gè)基于端口方式的輸入淅準(zhǔn)備被觸發(fā),立即運(yùn)行被時(shí)間篮灼,并且跳到9步忘古;
- 通知觀察者線程準(zhǔn)備休息;
- 讓線程休息诅诱,直到以下任何一個(gè)事件發(fā)生:
- 任何一個(gè)基于端口的輸入淅有事件發(fā)生髓堪;
- 任何一個(gè)計(jì)時(shí)器觸發(fā);
- 該Run Loop設(shè)置的過(guò)期時(shí)間過(guò)時(shí)了娘荡;
- 該Run Loop被明確地喚醒干旁;
- 通知觀察者線程被喚醒;
- 運(yùn)行待觸發(fā)的事件:
- 如果一個(gè)用戶自定義的計(jì)時(shí)器被觸發(fā)炮沐,運(yùn)行該計(jì)時(shí)器的事件争群,并重新運(yùn)行Run Loop.跳轉(zhuǎn)到第2步;
- 如果一個(gè)輸入源被觸發(fā)大年,傳遞該事件换薄;
- 如果該Run Loop被明確喚醒并且沒(méi)有超時(shí),重新運(yùn)行Run Loop循環(huán)翔试。跳轉(zhuǎn)到第2步轻要;
- 通知觀察者該Run Loop循環(huán)已經(jīng)退出。
輸入源(Input Source)
輸入源主要有三種:
- 基于端口的輸入源
- 自定義輸入源
- selector源
其中selector源就是常用的“performSelector...”方法垦缅。
定時(shí)源
定時(shí)源adpujiu是常用的NSTimer,定時(shí)器類冲泥;基機(jī)制也是基于Run Loop運(yùn)行的,只是在指定的間隔時(shí)間發(fā)送消息給需要處理的回調(diào)方法。
兩種方法:
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:4.0 targer:self selector:@selector(fireTimer:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
以及
[NSTimer scheduledTimerWithTimerInterval:10
terget:self
selector:@selector(fireTimer:)
userInfo:nil
repeats:YES];
不過(guò)需要注意的是凡恍,如果Run Loop沒(méi)有監(jiān)視跟定時(shí)淅相關(guān)模式幸冻,那么定時(shí)器將不會(huì)運(yùn)行。如果定時(shí)器開始時(shí)咳焚,Run Loop正在處理前面的事件,那么它會(huì)等Run Loop處理完了才開始庞溜。如果Run Loop不再運(yùn)行革半,那么定時(shí)器也永遠(yuǎn)不再啟動(dòng)了。
使用方式
啟動(dòng)方式
- 無(wú)條件啟動(dòng)流码;
- 設(shè)置超時(shí)時(shí)間啟動(dòng)又官,如RunUntilDate方法;
- 指定某種模式下啟動(dòng)漫试,如RunMode:beforDate:方法六敬;
能出方法
- 啟動(dòng)時(shí)設(shè)定好設(shè)定超時(shí)時(shí)間;
- 顯式的停止Run Loop(調(diào)用CFRunLoopStop函數(shù))驾荣;
啟動(dòng)模式
可用模式有 5 種外构,一般常用的都是default;
- default
- connection
- modal
- event tracking
- common modes
例子
在新線程中,注冊(cè)一個(gè)Run Loop的觀察者播掷,監(jiān)聽每次循環(huán)過(guò)程的所有事件审编;同時(shí)啟動(dòng)一個(gè)定時(shí)器;從日記中可以看到整個(gè)Run Loop 的過(guò)程歧匈,包括啟動(dòng)和結(jié)束垒酬、每次定時(shí)器喚醒時(shí)前后的事件、沒(méi)有任何任務(wù)進(jìn)入休眠的狀態(tài)件炉。由此可以看出勘究,Run Loop能更加精細(xì)地跟整個(gè)線程的運(yùn)行過(guò)程交互。
- (void)viewDidLoad {
[super viewDidLoad];
// 在新線程中運(yùn)行:
[self performSelectorInBackground:@selector(testRunLoop) withObject:ni;];
}
- (void)testRunloop {
// 獲取當(dāng)前線程的Run Loop
NSRunLoop *myRunLoop = [NSRunLoop currentRunLoop];
// 創(chuàng)建一個(gè)Run Loop 觀察者對(duì)象斟冕;觀察事件為每次循環(huán)的所有活動(dòng)口糕;
CFRunLoopObserverContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context);
if (observer)
{
// 將Cocoa的NSRunLoop類型轉(zhuǎn)換成Core Foundation的CFRunLoopRef類型
CFRunLoopRef cfLoop = [myRunLoop getCFRunLoop];
// 添加觀察才對(duì)象到該Run Loop 上
CFRunLoopAddObserver(cfLoop, observer, kCFRunLoopDefaultMode);
}
// 創(chuàng)建定時(shí)器,每0.1秒觸發(fā)
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self
selector:@selector(doFireTimer:) userInfo:nil repeats:YES];
// 重復(fù)啟動(dòng)Run Loop 5次
NSInteger loopCount = 5;
do {
//啟動(dòng) Run Loop 開始循環(huán)磕蛇,直到指定的時(shí)間才結(jié)束走净,這里就是持續(xù)1秒;
//當(dāng)調(diào)用RunUnitDate方法時(shí)孤里,觀察者檢測(cè)到循環(huán)已經(jīng)啟動(dòng)伏伯,開始根據(jù)循環(huán)的各個(gè)階段的事件,調(diào)用上面注冊(cè)的myRunLoopObserver回調(diào)函數(shù)捌袜。
[myRunLoop runUntiDate:[NSDate dateWithTimIntervalSinceNow:1]];
// 運(yùn)行完之后说搅,會(huì)再一次調(diào)用回調(diào)函數(shù),狀態(tài)是KFRunLoopExit,表示循環(huán)結(jié)束虏等。
loopCount--;
} while(loopCount);
NSlog(@"The End.");
}
- (void)doFireTimer:(NSTimer *)timer
{
NSLog(@"fire timer");
}
// Run loop觀察者的回調(diào)函數(shù):
void myRunLoopObserver(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"run loop entry");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"run loop before timers");
break;
case kCFRunLoopBeforeSources:
NSLog(@"run loop before sources");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"run loop before waiting");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"run loop after waiting");
break;
case kCFRunLoopExit:
NSLog(@"run loop exit");
break;
default:
break;
}
}