NSTimer 運(yùn)行機(jī)制

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ù)的工作。


runloop.jpg

4.觀察者


Run Loop對(duì)象的循環(huán)過(guò)程中可以添加觀察者對(duì)象辨嗽。整個(gè)Run Loop 在運(yùn)行過(guò)程中發(fā)生的事件具體如下:

  1. 通知觀察者Run Loop開始了膘怕;
  2. 通知觀察者任何預(yù)設(shè)好的時(shí)間已經(jīng)觸發(fā);
  3. 通知觀察者任何接入端口的輸入源準(zhǔn)備被觸發(fā)召庞;
  4. 觸發(fā)任何非端口方式接入的輸入源岛心;
  5. 如果有任何一個(gè)基于端口方式的輸入淅準(zhǔn)備被觸發(fā),立即運(yùn)行被時(shí)間篮灼,并且跳到9步忘古;
  6. 通知觀察者線程準(zhǔn)備休息;
  7. 讓線程休息诅诱,直到以下任何一個(gè)事件發(fā)生:
    1. 任何一個(gè)基于端口的輸入淅有事件發(fā)生髓堪;
    2. 任何一個(gè)計(jì)時(shí)器觸發(fā);
    3. 該Run Loop設(shè)置的過(guò)期時(shí)間過(guò)時(shí)了娘荡;
    4. 該Run Loop被明確地喚醒干旁;
  8. 通知觀察者線程被喚醒;
  9. 運(yùn)行待觸發(fā)的事件:
    1. 如果一個(gè)用戶自定義的計(jì)時(shí)器被觸發(fā)炮沐,運(yùn)行該計(jì)時(shí)器的事件争群,并重新運(yùn)行Run Loop.跳轉(zhuǎn)到第2步;
    2. 如果一個(gè)輸入源被觸發(fā)大年,傳遞該事件换薄;
    3. 如果該Run Loop被明確喚醒并且沒(méi)有超時(shí),重新運(yùn)行Run Loop循環(huán)翔试。跳轉(zhuǎn)到第2步轻要;
  10. 通知觀察者該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;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末弄唧,一起剝皮案震驚了整個(gè)濱河市适肠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌候引,老刑警劉巖侯养,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異澄干,居然都是意外死亡逛揩,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門麸俘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)辩稽,“玉大人,你說(shuō)我怎么就攤上這事从媚〕研梗” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵拜效,是天一觀的道長(zhǎng)喷众。 經(jīng)常有香客問(wèn)我,道長(zhǎng)紧憾,這世上最難降的妖魔是什么侮腹? 我笑而不...
    開封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮稻励,結(jié)果婚禮上父阻,老公的妹妹穿的比我還像新娘。我一直安慰自己望抽,他們只是感情好加矛,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著煤篙,像睡著了一般斟览。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上辑奈,一...
    開封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天苛茂,我揣著相機(jī)與錄音,去河邊找鬼鸠窗。 笑死妓羊,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的稍计。 我是一名探鬼主播躁绸,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了净刮?” 一聲冷哼從身側(cè)響起剥哑,我...
    開封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎淹父,沒(méi)想到半個(gè)月后株婴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡暑认,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年困介,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片穷吮。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖饥努,靈堂內(nèi)的尸體忽然破棺而出捡鱼,到底是詐尸還是另有隱情,我是刑警寧澤酷愧,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布驾诈,位于F島的核電站,受9級(jí)特大地震影響溶浴,放射性物質(zhì)發(fā)生泄漏乍迄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一士败、第九天 我趴在偏房一處隱蔽的房頂上張望闯两。 院中可真熱鬧,春花似錦谅将、人聲如沸漾狼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)逊躁。三九已至,卻和暖如春隅熙,著一層夾襖步出監(jiān)牢的瞬間稽煤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工囚戚, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留酵熙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓驰坊,卻偏偏與公主長(zhǎng)得像绿店,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容