iOS RunLoop按声,面試裝13的神器

圖層0@2x.png

今天我們來(lái)學(xué)習(xí)下iOS中一個(gè)較為重要的核心--RunLoop裹粤。其實(shí)我們對(duì)RunLoop既熟悉又陌生终蒂。熟悉是因?yàn)槲覀冊(cè)陂_發(fā)中時(shí)不時(shí)的都會(huì)用到它蜂林,陌生是因?yàn)樗^為底層遥诉,我們對(duì)它的了解不是多拇泣。今天我們就一起來(lái)揭開RunLoop神秘的面紗,對(duì)他進(jìn)行一個(gè)較為簡(jiǎn)單的介紹矮锈。通過(guò)我的簡(jiǎn)單介紹霉翔,如果大家能對(duì)它有一定的了解和認(rèn)識(shí),那無(wú)論是在平時(shí)的工作中苞笨,還是在以后的面試中债朵,它都是我們能拿上臺(tái)面的一個(gè)利器。好瀑凝,廢話不多說(shuō)序芦,進(jìn)入正題吧。

1.RunLoop的簡(jiǎn)單介紹

1.1 RunLoop的基本概念

那么到底是什么RunLoop呢粤咪,從字面意思上可以得知谚中,他就是“跑圈”,翻譯的雅一點(diǎn)就是“運(yùn)行的循環(huán)”寥枝。在iOS SDK中宪塔,RunLoop實(shí)際上也是一個(gè)對(duì)象,這個(gè)對(duì)象在循環(huán)的處理程序在運(yùn)行過(guò)程中發(fā)出各種事件囊拜,例如某筐,用戶點(diǎn)擊了屏幕(TouchEvent),UI界面的刷新事件冠跷,定時(shí)器事件(Timer)南誊,Selector事件等等。當(dāng)我們將我們的程序退出到后臺(tái)蜜托,注意只是退出到后臺(tái)弟疆,并沒(méi)有terminate,這時(shí)RunLoop不會(huì)處理任何事件盗冷,此時(shí)為了節(jié)省CPU的資源怠苔,RunLoop會(huì)自動(dòng)進(jìn)入休眠模式。

1.2 RunLoop和線程的關(guān)系

當(dāng)我們談?wù)摰絉unLoop仪糖,其實(shí)我們就會(huì)提到與之息息相關(guān)的東西柑司,線程。我們知道锅劝,線程的作用是用來(lái)執(zhí)行一個(gè)或者多個(gè)特定任務(wù)的攒驰,一般情況下,某個(gè)線程的任務(wù)執(zhí)行完畢故爵,他就return掉玻粪。那么,我們?yōu)榱俗尵€程能夠不斷執(zhí)行我們指派的,或者系統(tǒng)分配任務(wù)劲室,讓其任務(wù)執(zhí)行完后并不return伦仍,這時(shí)候我們就用到了RunLoop。
一個(gè)線程對(duì)應(yīng)唯一一個(gè)RunLoop對(duì)象很洋,這點(diǎn)很重要充蓝。但是往往,RunLoop并不能保證我們線程安全喉磁,因?yàn)槲覀冎荒茉诋?dāng)前線程中操作與之對(duì)應(yīng)的RunLoop對(duì)象谓苟,而不能在當(dāng)前線程中去操作其他線程的RunLoop對(duì)象。子線程的RunLoop對(duì)象协怒,需要我們手動(dòng)去創(chuàng)建和維護(hù)涝焙,當(dāng)線程結(jié)束時(shí),它也就銷毀了孕暇。這里我們就會(huì)想到仑撞,那么主線程mainThread的RunLoop,是誰(shuí)創(chuàng)建的呢芭商?其實(shí)這個(gè)問(wèn)題有點(diǎn)白癡 : ]派草,當(dāng)然是系統(tǒng)自動(dòng)創(chuàng)建的。

1.3 主線程的RunLoop

當(dāng)我們創(chuàng)建一個(gè)新工程的時(shí)候铛楣,我們找他的main.m近迁,我們可以看到他的代碼很簡(jiǎn)單,就一個(gè)main方法簸州,代碼如下:

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

這個(gè)方法就是我們程序的入口鉴竭,在這個(gè)方法中,首先標(biāo)注了一個(gè)自動(dòng)釋放池autoreleasepool岸浑,然后再返回一個(gè)系統(tǒng)函數(shù)UIApplicationMain執(zhí)行結(jié)果搏存,其實(shí)這個(gè)UIApplicationMain函數(shù)就是為我們的程序創(chuàng)建了一個(gè)主線程的RunLoop,只要我們的程序不crash掉矢洲,那他就會(huì)一直運(yùn)行循環(huán)璧眠,直到我們將程序退出到后臺(tái),或者terminate掉读虏,它才會(huì)被掛起或是被銷毀掉责静。
我們?cè)賮?lái)看看,官方文檔上是如何圖解RunLoop的盖桥,如下圖所示:

640.jpeg

從上圖中我們可以看出灾螃,RunLoop實(shí)際就是一個(gè)循環(huán),它會(huì)一直監(jiān)聽(tīng)輸入源InputSources和定時(shí)源TimerSources揩徊,一旦接收到事件腰鬼,它便會(huì)對(duì)其進(jìn)行處理嵌赠,如果長(zhǎng)時(shí)間沒(méi)有檢測(cè)到事件,那么它會(huì)自動(dòng)進(jìn)入休眠狀態(tài)熄赡。

2.RunLoop相關(guān)類介紹

我們要想對(duì)RunLoop有跟進(jìn)一步的理解姜挺,我則需要對(duì)CoreFoundation框架下的與RunLoop相關(guān)的5個(gè)類,他們分別是:

  • CFRunLoopRef:RunLoop對(duì)象
  • CFRunLoopMode:RunLoop的運(yùn)行模式
  • CFRunLoopSourceRef:RunLoop監(jiān)聽(tīng)的輸入源(事件源)
  • CFRunLoopTimerRef:RunLoop監(jiān)聽(tīng)的定時(shí)源(Timer源)
  • CFRunLoopObserverRef:觀察者本谜,監(jiān)聽(tīng)RunLoop狀態(tài)的變化
    接下來(lái)我們就來(lái)詳細(xì)的說(shuō)下上面的這5個(gè)類的具體內(nèi)容和他們之間的關(guān)系初家。

首先我們來(lái)看下5個(gè)類的關(guān)系偎窘,如下圖所示乌助,

640.jpeg

我們來(lái)簡(jiǎn)單解釋下他們之間的關(guān)系,一個(gè)CFRunLoopRef對(duì)象包含若干個(gè)CFRunLoopMode運(yùn)行模式陌知,每個(gè)運(yùn)行模式又包含若干個(gè)CFRunLoopSourceRef輸入源他托、CFRunLoopTimerRef定時(shí)源、CFRunLoopObserverRef觀察者仆葡。這里需要注意的是赏参,雖然一個(gè)RunLoopRef對(duì)象可以包含若干個(gè)CFRunLoopMode運(yùn)行模式,但是該RunLoopRef對(duì)象當(dāng)前運(yùn)行的模式只能是指定的它包含的若干個(gè)CFRunLoopMode運(yùn)行模式中的一個(gè)沿盅,那么這個(gè)被指定的運(yùn)行模式就叫做“當(dāng)前運(yùn)行模式”CurrentMode把篓。但是CurrentMode是可以進(jìn)行切換的,如果需要切換運(yùn)行模式腰涧,則需要退出當(dāng)前的Loop韧掩,然后重新指定CFRunLoopMode。這樣做的目的其實(shí)也很容明白窖铡,因?yàn)槊恳粋€(gè)CFRunLoopMode包含若干個(gè)CFRunLoopSourceRef疗锐、CFRunLoopTimerRefCFRunLoopObserverRef费彼,為了能有效的隔離不同CFRunLoopModeCFRunLoopSourceRef滑臊、CFRunLoopTimerRef罕模、CFRunLoopObserverRef涧偷,使其不同CFRunLoopSourceRef互不影響凛驮,所以一個(gè)CFRunLoopRef只能指定一個(gè)CurrentMode瓦哎。
接下來(lái)摔敛,我們將逐一詳細(xì)的來(lái)介紹與RunLoop密切相關(guān)的這5個(gè)類棵帽。

2.1 CFRunLoopRef

CFRunLoopRef是CoreFoundation內(nèi)庫(kù)中RunLoop對(duì)象類饱苟。我們可以以下方式來(lái)獲取CFRunLoopRef對(duì)象腊凶。

//  獲取當(dāng)前線程的CFRunLoopRef
CFRunLoopRef runLoopRef = CFRunLoopGetCurrent();
//  獲取主線程的CFRunLoopRef
CFRunLoopRef mainRunLoopRef = CFRunLoopGetMain();

NSRunLoop是Foundation內(nèi)庫(kù)中RunLoop的對(duì)象芙粱。我們同樣可以使用以下方法來(lái)回去NSRunLoop對(duì)象

//  獲取當(dāng)前RunLoop
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
//  獲取主線程的RunLoop
NSRunLoop *mainRunLoop = [NSRunLoop mainRunLoop];
2.2 CFRunLoopMode

iOS系統(tǒng)第一了很多種運(yùn)行模式祭玉,下面我們一一簡(jiǎn)單的介紹:

  • kCFRunLoopDefaultMode:App默認(rèn)運(yùn)行模式,通常我們的主線程的當(dāng)前模式就是這個(gè)模式春畔。
  • UITrackingRunLoopMode:跟蹤用戶交互事件(用于ScrollView追蹤觸摸滑動(dòng)脱货,保證界面滑動(dòng)時(shí)不受其他Mode的影響)岛都。
  • UIInitializationRunLoopMode:當(dāng)App剛啟動(dòng)的時(shí)候,系統(tǒng)將進(jìn)入這個(gè)模式振峻,啟動(dòng)完成后將切換到另外的運(yùn)行模式臼疫。
  • GSEventReceiveRunLoopMode:接受系統(tǒng)內(nèi)部事件,通常我們?cè)匍_發(fā)的時(shí)候很少用這個(gè)運(yùn)行模式扣孟。
  • kCFRunLoopCommonModes:偽模式烫堤,不是一種真正的運(yùn)行模式,我們?cè)诤竺娴膬?nèi)容中會(huì)介紹到這個(gè)運(yùn)行模式凤价。
    以上提及到的5種運(yùn)行模式中鸽斟,我們?cè)陂_發(fā)過(guò)中常用到的是kCFRunLoopDefaultModeUITrackingRunLoopMode利诺、kCFRunLoopCommonModes富蓄,那么下面就來(lái)講講這三種運(yùn)行模式的具體用法。
2.3 CFRunLoopTimerRef (Timer事件源)

CFRunLoopTimerRef是我們上文提及到的定時(shí)源慢逾,他是RunLoop監(jiān)聽(tīng)的事件源之一立倍,在RunLoop相關(guān)類的關(guān)系圖中我們提過(guò)它。我們可以簡(jiǎn)單的將其理解為基于時(shí)間的觸發(fā)器侣滩。我們也可以將其理解我們開發(fā)時(shí)常用的NSTimer口注。
接下來(lái)我們舉一個(gè)簡(jiǎn)單的例子來(lái)演示一下CFRunLoopModeCFRunLoopTimerRef相結(jié)合的簡(jiǎn)單用法。
1.首先我們?cè)賃IViewController.view上添加一個(gè)scrollView, 在scrollView上我們?cè)偬砑右粋€(gè)Label, 然后我們?cè)賾屑虞d一個(gè)定時(shí)器君珠, 然后我們啟動(dòng)定時(shí)器寝志,定時(shí)器將每隔1秒修改Label.text,代碼如下:

- (void)viewDidLoad {
  [self.view addSubview:self.scrollView];
  [self.scrollView addSubview: self.label];
  [self.timer fire];
}

- (void)on_timer {
    _num += 1;
    self.label.text = [NSString stringWithFormat:@"%zd", _num];
}

//  懶加載一個(gè)定時(shí)器
- (NSTimer *)timer {
    if (!_timer) {
        _timer = ({
            NSTimer *timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(on_timer) userInfo:nil repeats:YES];
            timer;
        });
    }
    return _timer;
}

我們運(yùn)行我們的程序葛躏,我們不做任何操作澈段,我們可以看到我們的label.text會(huì)每隔一秒鐘就變化一次。如果此時(shí)我們滑動(dòng)我們的scrollView舰攒,我們就會(huì)發(fā)現(xiàn)label.text將不會(huì)再發(fā)生改變败富,當(dāng)我們停止滑動(dòng),手指離開屏幕的時(shí)候摩窃,label.text又開始繼續(xù)發(fā)生改變兽叮。那么到底是什么原因到導(dǎo)致這樣的結(jié)果呢?
當(dāng)我們?cè)俪跏蓟疶imer的時(shí)候猾愿,沒(méi)有手動(dòng)將Timer注入到某種運(yùn)行模式下鹦聪,此時(shí)系統(tǒng)會(huì)默認(rèn)將其注入到NSDefaultRunLoopMode,所以我們運(yùn)行程序蒂秘,且不做任何操作的時(shí)候泽本,此時(shí)RunLoop是當(dāng)currentMode是NSDefaultRunLoopMode,Timer正常工作姻僧。當(dāng)我們滑動(dòng)scrollView的時(shí)候规丽,此時(shí)系統(tǒng)會(huì)將currentMode切換到UITrackingRunLoopMode蒲牧,我們的Timer并沒(méi)有注入到這個(gè)模式下,所以Timer失效赌莺。當(dāng)我們的滑動(dòng)結(jié)束冰抢,手指離開屏幕的時(shí)候,currentMode又切換到NSDefaultRunLoopMode艘狭,此時(shí)Timer又正常工作挎扰。那么我么在初始化Timer的時(shí)候,可以將其注入到指定的運(yùn)行模式下嗎巢音?是可以的遵倦,代碼如下:

//  手動(dòng)的將Timer注入到UITrackingRunLoopMode模式下
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];

此時(shí)我們手動(dòng)將Timer注入到UITrackingRunLoopMode,然后我們運(yùn)行程序港谊,其實(shí)顯而易見(jiàn)骇吭,此時(shí)我們不做任何操作橙弱,Timer是不會(huì)正常工作的歧寺,當(dāng)我們滑動(dòng)scrollView的時(shí)候,Timer開始正常工作棘脐,原因與上同理斜筐。這里我來(lái)問(wèn)個(gè)S13問(wèn)題:), 難倒我們就不能在這兩種模式下讓Timer都能正常工作嗎蛀缝?(這個(gè)問(wèn)題真的S13)顷链。
答案是當(dāng)然可以的,其實(shí)我是為了引出偽模式(kCFRunLoopCommonModes)屈梁。kCFRunLoopCommonModes其實(shí)都不能算是一種運(yùn)行模式嗤练,它只是一種標(biāo)記,它可以對(duì)其他運(yùn)行模型進(jìn)行標(biāo)記在讶。說(shuō)道這里煞抬,大家可能都明白了系統(tǒng)的NSDefaultRunLoopModeUITrackingRunLoopMode都是被標(biāo)記上Common modes。所以我們只需要將我們的timer手動(dòng)注入到kCFRunLoopCommonModes下构哺,那么此時(shí)無(wú)論系統(tǒng)的currentMode是default還是UITracking革答,timer將都能正常工作。修改代碼如下:

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

這里我們說(shuō)道了Timer曙强,那么我就順便就說(shuō)說(shuō)這個(gè)timer
NSTimer有兩個(gè)便利構(gòu)造函數(shù)入下:

//  構(gòu)造函數(shù)1
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
//  構(gòu)造函數(shù)2
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;

構(gòu)造函數(shù)1残拐,返回的timer對(duì)象會(huì)自動(dòng)注入到NSDefaultRunLoopMode,其實(shí)相當(dāng)于構(gòu)造函數(shù)1碟嘴,再手動(dòng)注入到運(yùn)行模式中溪食,例如:

NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(...) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
2.4 CFRunLoopSourceRef (Input事件源)

所有的事件源有兩種分類方法,我們來(lái)具體看看他哪兩種分類:

  1. 按照官方文檔來(lái)分類(和前面我貼出的官方RunLoop模型圖里一樣):
    Port-Based Sources :基于端口事件
    Custom Input Sources:用戶自定義事件

  2. 按照函數(shù)調(diào)用棧來(lái)分類:
    Source0:非基于端口事件
    Source1:基于端口娜扇,通過(guò)內(nèi)核和其他線程通信错沃、接收边败、分發(fā)系統(tǒng)事件

其實(shí)根本一點(diǎn)講,這兩種分類是沒(méi)有區(qū)別的捎废,只不過(guò)呢笑窜,第一種是根據(jù)官方給出的理論來(lái)進(jìn)行分類的。第二是我們?cè)趯?shí)際應(yīng)用中通過(guò)函數(shù)的調(diào)用來(lái)進(jìn)行分類的登疗。

2.5 CFRunLoopObserverRef (I觀察者類)

RunLoop的狀態(tài)會(huì)根據(jù)用戶的操作以及程序狀態(tài)的變化而發(fā)生改變排截,通常我們會(huì)去監(jiān)聽(tīng)RunLoop的實(shí)時(shí)變化,此時(shí)我們就會(huì)用到一個(gè)觀察者類CFRunLoopObserverRef辐益。RunLoop的狀態(tài)集是以一個(gè)枚舉類型來(lái)表示断傲,入下面代碼所示:

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),                       // 即將進(jìn)入Loop:1
    kCFRunLoopBeforeTimers = (1UL << 1),          // 即將處理Timer:2    
    kCFRunLoopBeforeSources = (1UL << 2),        // 即將處理Source:4
    kCFRunLoopBeforeWaiting = (1UL << 5),         // 即將進(jìn)入休眠:32
    kCFRunLoopAfterWaiting = (1UL << 6),            // 即將從休眠中喚醒:64
    kCFRunLoopExit = (1UL << 7),                         // 即將從Loop中退出:128
    kCFRunLoopAllActivities = 0x0FFFFFFFU       // 監(jiān)聽(tīng)全部狀態(tài)改變  
};

下面我們用個(gè)小小的例子來(lái)觀察下,RunLoop的狀態(tài)到底是怎么變化的智政,以及我們?nèi)绾问褂?code>CFRunLoopObserverRef來(lái)監(jiān)聽(tīng)RunLoop的狀態(tài)變化认罩。
首先我們?cè)趘iewDidLoad中添加如下代碼:

CFRunLoopObserverRef observerRef =
    CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(),
                                       kCFRunLoopAllActivities,
                                       YES,
                                       0,
                                       ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        NSLog(@"監(jiān)聽(tīng)到RunLoop發(fā)生改變---%zd",activity);
    });
    
    // 2.append Observer to RunLoop
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observerRef, kCFRunLoopDefaultMode);
    
    // 3.release observer
    CFRelease(observerRef);

然后我們運(yùn)行我們的程序,然后控制臺(tái)會(huì)打印出一大串信息续捂,這里我只貼出最后部分打印信息:

2018-12-25 10:36:03.293175+0800 TestApp[12836:1611962] 監(jiān)聽(tīng)到RunLoop發(fā)生改變---2
2018-12-25 10:36:03.293363+0800 TestApp[12836:1611962] 監(jiān)聽(tīng)到RunLoop發(fā)生改變---4
2018-12-25 10:36:03.293517+0800 TestApp[12836:1611962] 監(jiān)聽(tīng)到RunLoop發(fā)生改變---32
2018-12-25 10:37:00.032407+0800 TestApp[12836:1611962] 監(jiān)聽(tīng)到RunLoop發(fā)生改變---64
2018-12-25 10:37:00.036248+0800 TestApp[12836:1611962] 監(jiān)聽(tīng)到RunLoop發(fā)生改變---2
2018-12-25 10:37:00.036335+0800 TestApp[12836:1611962] 監(jiān)聽(tīng)到RunLoop發(fā)生改變---4
2018-12-25 10:37:00.036392+0800 TestApp[12836:1611962] 監(jiān)聽(tīng)到RunLoop發(fā)生改變---32

我們可以看到垦垂,RunLoop的狀態(tài)是在不斷的變化的,他最后的狀態(tài)是32牙瓢,也就是我們上面的枚舉中的kCFRunLoopBeforeWaiting劫拗,RunLoop即將進(jìn)入休眠。

3.RunLoop的原理介紹

OK矾克,我們上面講解了RunLoop的基本概念页慷,以及與RunLoop相關(guān)的幾個(gè)類的介紹,接下來(lái)我們將具體的來(lái)說(shuō)一說(shuō)RunLoop的原理胁附。它到底是怎么運(yùn)作酒繁,希望通過(guò)下面講解,大家都可以了解RunLoop的內(nèi)部運(yùn)行邏輯控妻。

這里我貼一張我自己畫的圖州袒,簡(jiǎn)單的描述了一下RunLoop的運(yùn)行邏輯,如下圖所示:


屏幕快照 2018-12-25 上午11.38.44.png

上圖是我自己按照自己的理解畫的RunLoop運(yùn)行邏輯圖饼暑,可能對(duì)大家在理解RunLoop的運(yùn)行邏輯的時(shí)候有一點(diǎn)幫助稳析,下面我們來(lái)看看官方文檔對(duì)RunLoop的運(yùn)行邏輯是如何闡述的。

當(dāng)我們運(yùn)行我們的程序的時(shí)候弓叛,所有線程的RunLoop會(huì)同時(shí)被喚醒彰居,并且開始自動(dòng)處理之前未處理完的事件,通知也發(fā)出通知撰筷,通知其相應(yīng)的Observer陈惰。詳細(xì)的順序如下:

  1. 通知觀察者RunLoop已經(jīng)啟動(dòng)
  2. 通知觀察者即將要開始的定時(shí)器
  3. 通知觀察者任何即將啟動(dòng)的非基于端口的源
  4. 啟動(dòng)任何準(zhǔn)備好的非基于端口的源
  5. 如果基于端口的源準(zhǔn)備好并處于等待狀態(tài),立即啟動(dòng)毕籽;并進(jìn)入步驟9
  6. 通知觀察者線程進(jìn)入休眠狀態(tài)
  7. 將線程置于休眠知道任一下面的事件發(fā)生:
    某一事件到達(dá)基于端口的源
    定時(shí)器啟動(dòng)
    RunLoop設(shè)置的時(shí)間已經(jīng)超時(shí)
    RunLoop被顯示喚醒
  8. 通知觀察者線程將被喚醒
  9. 處理未處理的事件
    如果用戶定義的定時(shí)器啟動(dòng)抬闯,處理定時(shí)器事件并重啟RunLoop井辆。進(jìn)入步驟2
    如果輸入源啟動(dòng),傳遞相應(yīng)的消息
    如果RunLoop被顯示喚醒而且時(shí)間還沒(méi)超時(shí)溶握,重啟RunLoop杯缺。進(jìn)入步驟2
  10. 通知觀察者RunLoop結(jié)束。
    以上就是官方對(duì)RunLoop的運(yùn)行邏輯給出的說(shuō)明睡榆,我是直接用有道翻譯出來(lái)的萍肆,沒(méi)有去做校驗(yàn),大家可以對(duì)照我上面貼出的圖胀屿,大致看下RunLoop的運(yùn)行邏輯塘揣。

4.RunLoop開發(fā)應(yīng)用

上面說(shuō)一大堆,很多都是概念性東西宿崭,要想真正的掌握RunLoop我們還是得到實(shí)際運(yùn)用中亲铡。通過(guò)在實(shí)際開發(fā)過(guò)程中的實(shí)際應(yīng)用,我們對(duì)RunLoop的認(rèn)識(shí)才能更加深刻葡兑。

4.1 NSTimer的應(yīng)用

NSTimer奖蔓,我們上面的2.3CFRunLoopTimerRef (Timer事件源)中已經(jīng)提到過(guò)有關(guān)NSTimer和RunLoop的關(guān)系,這里呢铁孵,我們就不再重復(fù)了锭硼。

4.2 開啟常駐線程

在我們的實(shí)際開發(fā)過(guò)程中,有時(shí)候可能會(huì)遇到在后臺(tái)頻繁操作的的一些需求蜕劝,例如,文件現(xiàn)在轰异,音樂(lè)播放岖沛,實(shí)時(shí)定位等等,這些操作經(jīng)常會(huì)在子線程做一些耗時(shí)的操作搭独,此時(shí)婴削,我們就可以應(yīng)用RunLoop將這些耗時(shí)的子線程放到我們的常駐線程中去。

具體的操作室牙肝,代碼如下:
1.首先我們開啟一條子線程:

- (void)viewDidLoad {
    [super viewDidLoad];

    // 初始化線程唉俗,執(zhí)行任務(wù)run1
    self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(run1) object:nil];
    [self.thread start];    
}

- (void) run1
{
    NSLog(@"----run1-----");

    // 添加下邊兩句代碼,就可以開啟RunLoop配椭,之后self.thread就變成了常駐線程虫溜,可隨時(shí)添加任務(wù),并交于RunLoop處理
    [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] run];

    // 如果開啟RunLoop股缸,則不會(huì)執(zhí)行下面這句打印衡楞,因?yàn)镽unLoop開啟了循環(huán)。
    NSLog(@"未開啟RunLoop");
}

我們運(yùn)行我們的程序敦姻,控制并沒(méi)有打印 "未開啟RunLoop"瘾境, 說(shuō)明我們開啟了一條常駐線程歧杏,此時(shí)我們?cè)偻€程中添加一些子任務(wù)代碼如下:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{   
    // 在self.thread的線程中執(zhí)行任務(wù)run2
    [self performSelector:@selector(run2) onThread:self.thread withObject:nil waitUntilDone:NO];
}

- (void) run2
{
    NSLog(@"----run2------");
}

此時(shí)我們運(yùn)行我們的程序,每當(dāng)我們點(diǎn)擊我們的屏幕的時(shí)候迷守,控制臺(tái)都會(huì)打印“----run2------”犬绒,這樣我們就實(shí)現(xiàn)了常駐線程的需求。當(dāng)然這里我只是簡(jiǎn)單的給大家演示了一下常駐線程的例子兑凿,那么大家可以根據(jù)自己不同是實(shí)際開發(fā)需求懂更,來(lái)進(jìn)行編碼,并將RunLoop特性發(fā)揮起來(lái)急膀。

以上就是對(duì)RunLoop的基本概念沮协,相關(guān)類,運(yùn)行邏輯卓嫂,以及實(shí)際應(yīng)用的簡(jiǎn)單介紹慷暂,當(dāng)然我個(gè)人是水平也是比較次,有不對(duì)的晨雳,或者不準(zhǔn)確的地方行瑞,希望大家留言指正。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末餐禁,一起剝皮案震驚了整個(gè)濱河市血久,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌帮非,老刑警劉巖氧吐,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異末盔,居然都是意外死亡筑舅,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門陨舱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)翠拣,“玉大人,你說(shuō)我怎么就攤上這事游盲∥竽梗” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵益缎,是天一觀的道長(zhǎng)谜慌。 經(jīng)常有香客問(wèn)我,道長(zhǎng)链峭,這世上最難降的妖魔是什么畦娄? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上熙卡,老公的妹妹穿的比我還像新娘杖刷。我一直安慰自己,他們只是感情好驳癌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布滑燃。 她就那樣靜靜地躺著,像睡著了一般颓鲜。 火紅的嫁衣襯著肌膚如雪表窘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天甜滨,我揣著相機(jī)與錄音乐严,去河邊找鬼。 笑死衣摩,一個(gè)胖子當(dāng)著我的面吹牛昂验,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播艾扮,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼既琴,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了泡嘴?” 一聲冷哼從身側(cè)響起甫恩,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎酌予,沒(méi)想到半個(gè)月后磺箕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡霎终,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年滞磺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片莱褒。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖涎劈,靈堂內(nèi)的尸體忽然破棺而出广凸,到底是詐尸還是另有隱情,我是刑警寧澤蛛枚,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布谅海,位于F島的核電站,受9級(jí)特大地震影響蹦浦,放射性物質(zhì)發(fā)生泄漏扭吁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望侥袜。 院中可真熱鬧蝌诡,春花似錦、人聲如沸枫吧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)九杂。三九已至颁湖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間例隆,已是汗流浹背甥捺。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留镀层,地道東北人镰禾。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像鹿响,于是被迫代替她去往敵國(guó)和親羡微。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

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