runLoop 事件

1.runLoop 的意思

Run 表示運行,Loop 表示循環(huán)。結(jié)合在一起就是運行的循環(huán)的意思

  • RunLoop 實際上是一個對象,這個對象在循環(huán)中用來處理程序運行過程中出現(xiàn)的各種事件(比如說觸摸事件、UI刷新事件、定時器事件属提、Selector事件),從而保持程序的持續(xù)運行美尸。
  • RunLoop 在沒有事件處理的時候冤议,會使線程進入睡眠模式,從而節(jié)省 CPU 資源师坎,提高程序性能恕酸。

2. RunLoop跟線程

線程的作用是用來處理執(zhí)行特定的一個或者多個任務(wù),在默認情況下胯陋,線程執(zhí)行完就會退出蕊温。這時我們就需要采用一種方式來讓線程能夠不斷地處理任務(wù),并不退出遏乔。所以义矛,我們就有了 RunLoop。

  • 1.一個線程對應(yīng) 1 個 RunLoop,是一一對應(yīng)的關(guān)系盟萨。
    1. RunLoop并不保證線程安全凉翻,只能在當(dāng)前線程操作自己所對應(yīng)的 RunLoop,不能在當(dāng)前線程操作其他的 RunLoop。
  • 3.RunLoop 對象在第一次獲取 RunLoop 時創(chuàng)建捻激,銷毀則是在線程結(jié)束的時候
  • 4.主線程的 RunLoop 對象系統(tǒng)自動幫助我們創(chuàng)建好了制轰,而子線程的 RunLoop對象需要我們主動創(chuàng)建和維護

3 RunLoop 相關(guān)類

  • 1.CFRunLoopRef :代表 RunLoop 的對象
    1. CFRunLoopModeRef:代表 runloop的運行模式
    1. CFRunLoopSourceRef:RunLoop 模型圖中提到的輸入源 / 事件源
    1. CFRunLoopTimerRef: RunLoop 模型圖中提到的定時源
    1. CFRunLoopObserverRef:觀察者,能夠監(jiān)聽 RunLoop 的狀態(tài)改變

一個RunLoop對象(CFRunLoopRef)中包含若干個運行模式(CFRunLoopModeRef)铺罢。而每一個運行模式下又包含若干個輸入源(CFRunLoopSourceRef)艇挨、定時源(CFRunLoopTimerRef)残炮、觀察者(CFRunLoopObserverRef)

  • 每次 runloop 啟動時韭赘,只能指定其中一個運行模式(CFRunLoopModeRef),這個運行模式(CFRunLoopModeRef)被稱作當(dāng)前運行模式(CurrentMode)势就。
  • 如果需要切換運行模式(CFRunLoopModeRef)泉瞻,只能退出當(dāng)前 Loop脉漏,再重新指定一個運行模式(CFRunLoopModeRef)進入。
  • 這樣做主要是為了分隔開不同組的輸入源(CFRunLoopSourceRef)袖牙、定時源(CFRunLoopTimerRef)侧巨、觀察者(CFRunLoopObserverRef),讓其互不影響 鞭达。
3.1 CFRunLoopRef 類

CFRunLoopRef 是 Core Foundation 框架下 RunLoop 對象類司忱。我們可通過以下方式來獲取 RunLoop 對象:

  • Core Foundation

    • CFRunLoopGetCurrent(); // 獲得當(dāng)前線程的 RunLoop 對象
    • CFRunLoopGetMain(); // 獲得主線程的 RunLoop 對
      在Foundation 框架下獲取 RunLoop 對象類的方法如下
  • Foundation

    • [NSRunLoop currentRunLoop]; // 獲得當(dāng)前線程的 RunLoop 對象

    • [NSRunLoop mainRunLoop]; // 獲得主線程的 RunLoop 對象

3.2 CFRunLoopModeRef

系統(tǒng)默認定義了多種運行模式(CFRunLoopModeRef),如下:

kCFRunLoopDefaultMode:App的默認運行模式畴蹭,通常主線程是在這個運行模式下運行

UITrackingRunLoopMode:跟蹤用戶交互事件(用于 ScrollView 追蹤觸摸滑動坦仍,保證界面滑動時不受其他Mode影響)

UIInitializationRunLoopMode:在剛啟動App時第進入的第一個 Mode,啟動完成后就不再使用

GSEventReceiveRunLoopMode:接受系統(tǒng)內(nèi)部事件叨襟,通常用不到

kCFRunLoopCommonModes:偽模式繁扎,不是一種真正的運行模式(后邊會用到)

其中kCFRunLoopDefaultMode、UITrackingRunLoopMode糊闽、kCFRunLoopCommonModes是我們開發(fā)中需要用到的模式

3.3 CFRunLoopTimerRef

CFRunLoopTimerRef是定時源梳玫,理解為基于時間的觸發(fā)器,基本上就是NSTimer

下面我們來演示下CFRunLoopModeRef和CFRunLoopTimerRef結(jié)合的使用用法右犹,從而加深理解提澎。

    [super viewDidLoad];

    // 定義一個定時器,約定兩秒之后調(diào)用self的run方法
    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

    // 將定時器添加到當(dāng)前RunLoop的NSDefaultRunLoopMode下
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}

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

這時候我們發(fā)現(xiàn)如果我們不對模擬器進行任何操作的話念链,定時器會穩(wěn)定的每隔2秒調(diào)用run方法打印虱朵。但是當(dāng)我們拖動Text View滾動時,我們發(fā)現(xiàn):run方法不打印了钓账,也就是說NSTimer不工作了碴犬。而當(dāng)我們松開鼠標(biāo)的時候,NSTimer就又開始正常工作了梆暮。

這是因為:
當(dāng)我們不做任何操作的時候服协,RunLoop處于NSDefaultRunLoopMode下。
而當(dāng)我們拖動Text View的時候啦粹,RunLoop就結(jié)束NSDefaultRunLoopMode偿荷,切換到了UITrackingRunLoopMode模式下,這個模式下沒有添加NSTimer唠椭,所以我們的NSTimer就不工作了跳纳。
但當(dāng)我們松開鼠標(biāo)的時候,RunLoop就結(jié)束UITrackingRunLoopMode模式贪嫂,又切換回NSDefaultRunLoopMode模式寺庄,所以NSTimer就又開始正常工作了。

那難道我們就不能在這兩種模式下讓NSTimer都能正常工作嗎?

當(dāng)然可以斗塘,這就用到了我們之前說過的偽模式(kCFRunLoopCommonModes)赢织,這其實不是一種真實的模式,而是一種標(biāo)記模式馍盟,意思就是可以在打上Common Modes標(biāo)記的模式下運行于置。

那么哪些模式被標(biāo)記上了Common Modes呢?

NSDefaultRunLoopModeUITrackingRunLoopMode贞岭。

所以我們只要我們將NSTimer添加到當(dāng)前RunLoop的kCFRunLoopCommonModes(Foundation框架下為NSRunLoopCommonModes)下八毯,我們就可以讓NSTimer在不做操作和拖動Text View兩種情況下愉快的正常工作了。

具體做法就是講添加語句改為[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

既然講到了NSTimer瞄桨,這里順便講下NSTimer中的scheduledTimerWithTimeInterval方法和RunLoop的關(guān)系宪彩。添加下面的代碼:

[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

這句代碼調(diào)用了scheduledTimer返回的定時器,NSTimer會自動被加入到了RunLoop的NSDefaultRunLoopMode模式下讲婚。這句代碼相當(dāng)于下面兩句代碼:

NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
3.4 CFRunLoopSourceRef

CFRunLoopSourceRef是事件源(RunLoop模型圖中提到過)尿孔,CFRunLoopSourceRef有兩種分類方法。
runloop 模型圖
第一種按照官方文檔來分類(就像RunLoop模型圖中那樣):

Port-Based Sources(基于端口)

Custom Input Sources(自定義)

Cocoa Perform Selector Sources

第二種按照函數(shù)調(diào)用棧來分類:

Source0 :非基于Port

Source1:基于Port筹麸,通過內(nèi)核和其他線程通信活合,接收、分發(fā)系統(tǒng)事件

這兩種分類方式其實沒有區(qū)別物赶,只不過第一種是通過官方理論來分類白指,第二種是在實際應(yīng)用中通過調(diào)用函數(shù)來分類。

3.5CFRunLoopObserverRef

CFRunLoopObserverRef是觀察者酵紫,用來監(jiān)聽RunLoop的狀態(tài)改變

CFRunLoopObserverRef可以監(jiān)聽的狀態(tài)改變有以下幾種:

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

4. RunLoop原理

原理

具體的順序如下:

1.通知觀察者RunLoop已經(jīng)啟動

2.通知觀察者即將要開始的定時器

3.通知觀察者任何即將啟動的非基于端口的源

4.啟動任何準(zhǔn)備好的非基于端口的源

5.如果基于端口的源準(zhǔn)備好并處于等待狀態(tài)告嘲,立即啟動;并進入步驟9

6.通知觀察者線程進入休眠狀態(tài)

7.將線程置于休眠知道任一下面的事件發(fā)生:

  • 某一事件到達基于端口的源

  • 定時器啟動

  • RunLoop設(shè)置的時間已經(jīng)超時

  • RunLoop被顯示喚醒

8.通知觀察者線程將被喚醒

9.處理未處理的事件

  • 如果用戶定義的定時器啟動奖地,處理定時器事件并重啟RunLoop橄唬。進入步驟2

  • 如果輸入源啟動,傳遞相應(yīng)的消息

  • 如果RunLoop被顯示喚醒而且時間還沒超時参歹,重啟RunLoop仰楚。進入步驟2

10.通知觀察者RunLoop結(jié)束。

5. RunLoop實戰(zhàn)應(yīng)用

光弄懂是沒啥用的犬庇,能夠?qū)崙?zhàn)應(yīng)用才是硬道理僧界。下面講解一下RunLoop的幾種應(yīng)用。

4.1 NSTimer的使用
NSTimer的使用方法在講解CFRunLoopTimerRef類的時候詳細講解過臭挽,具體參考上邊 2.3 CFRunLoopTimerRef捂襟。

4.2 ImageView推遲顯示
有時候,我們會遇到這種情況:當(dāng)界面中含有UITableView欢峰,而且每個UITableViewCell里邊都有圖片葬荷。這時候當(dāng)我們滾動UITableView的時候涨共,如果有一堆的圖片需要顯示,那么可能會出現(xiàn)卡頓的現(xiàn)象闯狱。
4.3 后臺常駐線程(很常用)
我們在開發(fā)應(yīng)用程序的過程中,如果后臺操作特別頻繁抛计,經(jīng)常會在子線程做一些耗時操作(下載文件哄孤、后臺播放音樂等),我們最好能讓這條線程永遠常駐內(nèi)存吹截。

那么怎么做呢瘦陈?

添加一條用于常駐內(nèi)存的強引用的子線程,在該線程的RunLoop下添加一個Sources波俄,開啟RunLoop晨逝。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市懦铺,隨后出現(xiàn)的幾起案子捉貌,更是在濱河造成了極大的恐慌,老刑警劉巖冬念,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趁窃,死亡現(xiàn)場離奇詭異,居然都是意外死亡急前,警方通過查閱死者的電腦和手機醒陆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來裆针,“玉大人刨摩,你說我怎么就攤上這事∈蓝郑” “怎么了澡刹?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長耘婚。 經(jīng)常有香客問我像屋,道長,這世上最難降的妖魔是什么边篮? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任己莺,我火速辦了婚禮,結(jié)果婚禮上戈轿,老公的妹妹穿的比我還像新娘凌受。我一直安慰自己,他們只是感情好思杯,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布胜蛉。 她就那樣靜靜地躺著挠进,像睡著了一般。 火紅的嫁衣襯著肌膚如雪誊册。 梳的紋絲不亂的頭發(fā)上领突,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機與錄音案怯,去河邊找鬼君旦。 笑死,一個胖子當(dāng)著我的面吹牛嘲碱,可吹牛的內(nèi)容都是我干的金砍。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼麦锯,長吁一口氣:“原來是場噩夢啊……” “哼恕稠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起扶欣,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤鹅巍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后料祠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體昆著,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年术陶,在試婚紗的時候發(fā)現(xiàn)自己被綠了凑懂。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡梧宫,死狀恐怖接谨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情塘匣,我是刑警寧澤脓豪,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站忌卤,受9級特大地震影響扫夜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜驰徊,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一笤闯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧棍厂,春花似錦颗味、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽时呀。三九已至,卻和暖如春晶默,著一層夾襖步出監(jiān)牢的瞬間谨娜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工磺陡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留趴梢,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓仅政,卻偏偏與公主長得像垢油,于是被迫代替她去往敵國和親盆驹。 傳聞我的和親對象是個殘疾皇子圆丹,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

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

  • RunLoop 文章目錄 RunLoop簡介 1.1 什么是RunLoop? 1.2 RunLoop和線程 1.3...
    May_d8f1閱讀 285評論 0 1
  • 文章目錄 RunLoop簡介 1.1 什么是RunLoop躯喇? 1.2 RunLoop和線程 1.3 默認情況下主線...
    咖啡綠茶1991閱讀 287評論 0 0
  • 文章目錄RunLoop簡介1.1 什么是RunLoop辫封? 1.2 RunLoop和線程1.3 默認情況下主線程的R...
    lusen_b閱讀 396評論 0 2
  • 本文首發(fā)于我的個人博客:「程序員充電站」[https://itcharge.cn]文章鏈接:「傳送門」[https...
    ITCharge閱讀 60,155評論 50 539
  • 1.1 什么是 RunLoop? RunLoop 實際上是一個對象廉丽,這個對象在循環(huán)中用來處理程序運行過程中出現(xiàn)的各...
    言己言閱讀 748評論 0 0