自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

解密-神秘的RunLoop

移動(dòng)開(kāi)發(fā) iOS
一直以來(lái)RunLoop就是個(gè)神秘的領(lǐng)域,好多2.3年的開(kāi)發(fā)者都不能準(zhǔn)確的表述它的作用,說(shuō)它神秘,其實(shí)RunLoop并沒(méi)有大家想象中的那么神秘,那么不好理解,本文就帶大家好好剖析一下”神秘的RunLoop”。

引言

一直以來(lái)RunLoop就是個(gè)神秘的領(lǐng)域,好多2.3年的開(kāi)發(fā)者都不能準(zhǔn)確的表述它的作用,說(shuō)它神秘,其實(shí)RunLoop并沒(méi)有大家想象中的那么神秘,那么不好理解,本文就帶大家好好剖析一下”神秘的RunLoop”

什么是RunLoop

從字面上看

  • 運(yùn)行循環(huán)
  • 跑圈 

 

 

[[184147]] 

循環(huán)

基本作用

  • 保持程序的持續(xù)運(yùn)行(比如主運(yùn)行循環(huán))
  • 處理App中的各種事件(比如觸摸事件、定時(shí)器事件、Selector事件)
  • 節(jié)省CPU資源,提高程序性能:該做事時(shí)做事,該休息時(shí)休息

存在價(jià)值 

 

 

 

沒(méi)有RunLoop 

 

 

 

有RunLoop 

 

 

 

主運(yùn)行循環(huán)

  • 第14行代碼的UIApplicationMain函數(shù)內(nèi)部就啟動(dòng)了一個(gè)RunLoop
  • 所以UIApplicationMain函數(shù)一直沒(méi)有返回,保持了程序的持續(xù)運(yùn)行
  • 這個(gè)默認(rèn)啟動(dòng)的RunLoop是跟主線程相關(guān)聯(lián)的

RunLoop對(duì)象

  • iOS中有2套API來(lái)訪問(wèn)和使用RunLoop

Foundation

NSRunLoop

Core Foundation

CFRunLoopRef

  • NSRunLoop和CFRunLoopRef都代表著RunLoop對(duì)象
  • NSRunLoop是基于CFRunLoopRef的一層OC包裝,所以要了解RunLoop內(nèi)部結(jié)構(gòu),需要多研究CFRunLoopRef層面的API(Core Foundation層面)

RunLoop資料

  • 蘋果官方文檔

https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html

  • CFRunLoopRef是開(kāi)源的

http://opensource.apple.com/source/CF/CF-1151.16/

RunLoop與線程

每條線程都有唯一的一個(gè)與之對(duì)應(yīng)的RunLoop對(duì)象

主線程的RunLoop已經(jīng)自動(dòng)創(chuàng)建好了,子線程的RunLoop需要主動(dòng)創(chuàng)建

RunLoop在***次獲取時(shí)創(chuàng)建,在線程結(jié)束時(shí)銷毀

獲取RunLoop對(duì)象

  • Foundation
  1. [NSRunLoop currentRunLoop]; // 獲得當(dāng)前線程的RunLoop對(duì)象 
  2.  
  3. [NSRunLoop mainRunLoop]; // 獲得主線程的RunLoop對(duì)象  
  • Core Foundation
  1. CFRunLoopGetCurrent(); // 獲得當(dāng)前線程的RunLoop對(duì)象 
  2.  
  3. CFRunLoopGetMain(); // 獲得主線程的RunLoop對(duì)象  

RunLoop相關(guān)類

  • Core Foundation中關(guān)于RunLoop的5個(gè)類

CFRunLoopRef

CFRunLoopModeRef

CFRunLoopSourceRef

CFRunLoopTimerRef

CFRunLoopObserverRef

注:RunLoop如果沒(méi)有這些東西 會(huì)直接退出

CFRunLoopModeRef

  • CFRunLoopModeRef代表RunLoop的運(yùn)行模式

一個(gè) RunLoop 包含若干個(gè) Mode,每個(gè)Mode又包含若干個(gè)Source/Timer/Observer

每次RunLoop啟動(dòng)時(shí),只能指定其中一個(gè) Mode,這個(gè)Mode被稱作 CurrentMode

如果需要切換Mode,只能退出Loop,再重新指定一個(gè)Mode進(jìn)入

這樣做主要是為了分隔開(kāi)不同組的Source/Timer/Observer,讓其互不影響 

 

 

相關(guān)類 

相關(guān)類

系統(tǒng)默認(rèn)注冊(cè)了5個(gè)Mode:(前兩個(gè)跟***一個(gè)常用)

  • kCFRunLoopDefaultMode:App的默認(rèn)Mode,通常主線程是在這個(gè)Mode下運(yùn)行
  • UITrackingRunLoopMode:界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動(dòng),保證界面滑動(dòng)時(shí)不受其他 Mode 影響
  • UIInitializationRunLoopMode: 在剛啟動(dòng) App 時(shí)第進(jìn)入的***個(gè) Mode,啟動(dòng)完成后就不再使用
  • GSEventReceiveRunLoopMode: 接受系統(tǒng)事件的內(nèi)部 Mode,通常用不到
  • kCFRunLoopCommonModes: 這是一個(gè)占位用的Mode,不是一種真正的Mode

CFRunLoopSourceRef

  • CFRunLoopSourceRef是事件源(輸入源)
  • 按照官方文檔的分類

Port-Based Sources (基于端口,跟其他線程交互,通過(guò)內(nèi)核發(fā)布的消息)

Custom Input Sources (自定義)

Cocoa Perform Selector Sources (performSelector…方法)

  • 按照函數(shù)調(diào)用棧的分類

Source0:非基于Port的

Source1:基于Port的

Source0: event事件,只含有回調(diào),需要先調(diào)用CFRunLoopSourceSignal(source),將這個(gè) Source 標(biāo)記為待處理,然后手動(dòng)調(diào)用 CFRunLoopWakeUp(runloop) 來(lái)喚醒 RunLoop。

Source1: 包含了一個(gè) mach_port 和一個(gè)回調(diào),被用于通過(guò)內(nèi)核和其他線程相互發(fā)送消息,能主動(dòng)喚醒 RunLoop 的線程。

函數(shù)調(diào)用棧 

 

 

函數(shù)調(diào)用棧 

函數(shù)調(diào)用棧

CFRunLoopTimerRef

  • CFRunLoopTimerRef是基于時(shí)間的觸發(fā)器
  • 基本上說(shuō)的就是NSTimer(CADisplayLink也是加到RunLoop),它受RunLoop的Mode影響
  • GCD的定時(shí)器不受RunLoop的Mode影響

CFRunLoopObserverRef

  • CFRunLoopObserverRef是觀察者,能夠監(jiān)聽(tīng)RunLoop的狀態(tài)改變
  • 可以監(jiān)聽(tīng)的時(shí)間點(diǎn)有以下幾個(gè)

使用

  1. - (void)observer 
  2.  
  3.  
  4.     // 創(chuàng)建observer 
  5.  
  6.     CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(),kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { 
  7.  
  8.         NSLog(@"----監(jiān)聽(tīng)到RunLoop狀態(tài)發(fā)生改變---%zd", activity); 
  9.  
  10.     }); 
  11.  
  12.   
  13.  
  14.     // 添加觀察者:監(jiān)聽(tīng)RunLoop的狀態(tài) 
  15.  
  16.     CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode); 
  17.  
  18.   
  19.  
  20.     // 釋放Observer 
  21.  
  22.     CFRelease(observer); 
  23.  

 特別注意

  1. /* 
  2.  
  3.     CF的內(nèi)存管理(Core Foundation) 
  4.  
  5.     1.凡是帶有Create、Copy、Retain等字眼的函數(shù),創(chuàng)建出來(lái)的對(duì)象,都需要在***做一次release 
  6.  
  7.     * 比如CFRunLoopObserverCreate 
  8.  
  9.     2.release函數(shù):CFRelease(對(duì)象); 
  10.  
  11. */  

RunLoop處理邏輯

– 官方版 

 

 

  

 

 

 

邏輯

– 網(wǎng)友整理版 

 

 

 

網(wǎng)友版

注:進(jìn)入RunLoop前 會(huì)判斷模式是否為空,為空直接退出

RunLoop應(yīng)用

  • NSTimer
  • ImageView顯示
  • PerformSelector
  • 常駐線程
  • 自動(dòng)釋放池

1.NSTimer(最常見(jiàn)RunLoop使用)

  1. - (void)timer 
  2.  
  3.  
  4.     NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES]; 
  5.  
  6.     // 定時(shí)器只運(yùn)行在NSDefaultRunLoopMode下,一旦RunLoop進(jìn)入其他模式,這個(gè)定時(shí)器就不會(huì)工作 
  7.  
  8.     //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; 
  9.  
  10.   
  11.  
  12.     // 定時(shí)器只運(yùn)行在UITrackingRunLoopMode下,一旦RunLoop進(jìn)入其他模式,這個(gè)定時(shí)器就不會(huì)工作 
  13.  
  14.     //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode]; 
  15.  
  16.   
  17.  
  18.     // 定時(shí)器會(huì)跑在標(biāo)記為common modes的模式下 
  19.  
  20.     // 標(biāo)記為common modes的模式:UITrackingRunLoopMode和NSDefaultRunLoopMode兼容 
  21.  
  22.     [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; 
  23.  
  24.   
  1. - (void)timer2 
  2.  
  3.  
  4.     // 調(diào)用了scheduledTimer返回的定時(shí)器,已經(jīng)自動(dòng)被添加到當(dāng)前runLoop中,而且是NSDefaultRunLoopMode 
  5.  
  6.     NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nilrepeats:YES]; 
  7.  
  8.   
  9.  
  10.     // 修改模式 
  11.  
  12.     [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; 
  13.  
  14.  

場(chǎng)景還原

拖拽時(shí)模式由NSDefaultRunLoopMode 進(jìn)入 UITrackingRunLoopMode

此時(shí)如下圖: NSTimer 不再響應(yīng) 圖片停止輪播 

 

 

 

NSDefaultRunLoopMode模式

NSRunLoopCommonModes 模式下兩種模式都可運(yùn)行

此時(shí)如下圖: NSTimer 在兩個(gè)模式下都可正常運(yùn)行 

 

 

 

2.ImageView

需求:當(dāng)用戶在拖拽時(shí)(UI交互時(shí))不顯示圖片,拖拽完成時(shí)顯示圖片

方法1 監(jiān)聽(tīng)UIScrollerView滾動(dòng) (通過(guò)UIScrollViewDelegate監(jiān)聽(tīng),此處不再舉例)

方法2 RunLoop 設(shè)置運(yùn)行模式

  1. // 只在NSDefaultRunLoopMode模式下顯示圖片 
  2.  
  3.     [self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"placeholder"]afterDelay:3.0 inModes:@[NSDefaultRunLoopMode]];   

 

 

3.PerformSelector

 

inModes:設(shè)置運(yùn)行模式

4.常駐線程 (重要)

應(yīng)用場(chǎng)景:經(jīng)常在后臺(tái)進(jìn)行耗時(shí)操作,如:監(jiān)控聯(lián)網(wǎng)狀態(tài),掃描沙盒等 不希望線程處理完事件就銷毀,保持常駐狀態(tài)

***種(推薦)

開(kāi)啟

  1. - (void)run 
  2.  
  3.  
  4.   //addPort:添加端口(就是source)  forMode:設(shè)置模式 
  5.  
  6.    [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode]; 
  7.  
  8.   //啟動(dòng)RunLoop 
  9.  
  10.     [[NSRunLoop currentRunLoop] run]; 
  11.  
  12.   
  13.  
  14. /* 
  15.  
  16.   //另外兩種啟動(dòng)方式 
  17.  
  18.     [NSDate distantFuture]:遙遠(yuǎn)的未來(lái)  這種寫法跟上面的run是一個(gè)意思 
  19.  
  20.     [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; 
  21.  
  22.     不設(shè)置模式 
  23.  
  24.     [[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]]; 
  25.  
  26.   */ 
  27.  
  28.  

退出-退出當(dāng)前線程

  1. [NSThread exit]; 

第二種(奇葩法)

優(yōu)點(diǎn):退出RunLoop比較方便-定義個(gè)標(biāo)記 while(flag){…}

  1. - (void)run 
  2.  
  3.  
  4.     while (1) { 
  5.  
  6.         [[NSRunLoop currentRunLoop] run]; 
  7.  
  8.     } 
  9.  
  10.  

5.自動(dòng)釋放池 

 

 

 

在休眠前(kCFRunLoopBeforeWaiting)進(jìn)行釋放,處理事件前創(chuàng)建釋放池,中間創(chuàng)建的對(duì)象會(huì)放入釋放池

特別注意:

在啟動(dòng)RunLoop之前建議用 @autoreleasepool {…}包裹

意義:創(chuàng)建一個(gè)大釋放池,釋放{}期間創(chuàng)建的臨時(shí)對(duì)象,一般好的框架的作者都會(huì)這么做

  1. - (void)execute 
  2.  
  3.  
  4.     @autoreleasepool { 
  5.  
  6.         NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES]; 
  7.  
  8.         [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; 
  9.  
  10.         [[NSRunLoop currentRunLoop] run]; 
  11.  
  12.     } 
  13.  
  14.  

題外話:

以后為了增加用戶體驗(yàn) 在用戶UI交互的時(shí)候 不做事件處理 我們可以把需要做的操作放到NSDefaultRunLoopMode

補(bǔ)充:GCD定時(shí)器

一般的NSTimer定時(shí)器因?yàn)槭艿絉unLoop,會(huì)存在時(shí)間不準(zhǔn)時(shí)的情況.

上文有提到GCD不受RunLoop影響,下面簡(jiǎn)單的說(shuō)一下它的使用

  1. /** 定時(shí)器(這里不用帶*,因?yàn)閐ispatch_source_t就是個(gè)類,內(nèi)部已經(jīng)包含了*) */ 
  2.  
  3. @property (nonatomic, strong) dispatch_source_t timer; 
  4.  
  5.   
  6.  
  7. int count = 0; 
  8.  
  9. - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
  10.  
  11.  
  12.     // 獲得隊(duì)列 
  13.  
  14. //    dispatch_queue_t queue = dispatch_get_global_queue(0, 0); 
  15.  
  16.     dispatch_queue_t queue = dispatch_get_main_queue(); 
  17.  
  18.   
  19.  
  20.     // 創(chuàng)建一個(gè)定時(shí)器(dispatch_source_t本質(zhì)還是個(gè)OC對(duì)象) 
  21.  
  22.     self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); 
  23.  
  24.   
  25.  
  26.     // 設(shè)置定時(shí)器的各種屬性(幾時(shí)開(kāi)始任務(wù),每隔多長(zhǎng)時(shí)間執(zhí)行一次) 
  27.  
  28.     // GCD的時(shí)間參數(shù),一般是納秒 NSEC_PER_SEC(1秒 == 10的9次方納秒) 
  29.  
  30.     // 何時(shí)開(kāi)始執(zhí)行***個(gè)任務(wù) 
  31.  
  32.     // dispatch_time(DISPATCH_TIME_NOW, 3.0 * NSEC_PER_SEC) 比當(dāng)前時(shí)間晚3秒 
  33.  
  34.     dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)); 
  35.  
  36.     uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC); 
  37.  
  38.     dispatch_source_set_timer(self.timer, start, interval, 0); 
  39.  
  40.   
  41.  
  42.     // 設(shè)置回調(diào) 
  43.  
  44.     dispatch_source_set_event_handler(self.timer, ^{ 
  45.  
  46.         NSLog(@"------------%@", [NSThread currentThread]); 
  47.  
  48.         count++; 
  49.  
  50.   
  51.  
  52. //        if (count == 4) { 
  53.  
  54. //            // 取消定時(shí)器 
  55.  
  56. //            dispatch_cancel(self.timer); 
  57.  
  58. //            self.timer = nil; 
  59.  
  60. //        } 
  61.  
  62.     }); 
  63.  
  64.   
  65.  
  66.     // 啟動(dòng)定時(shí)器 
  67.  
  68.     dispatch_resume(self.timer); 
  69.  
  70.  

RunLoop面試題

經(jīng)常會(huì)有喜歡裝B的面試官,面試的時(shí)候就喜歡問(wèn)RunLoop,其實(shí)他真的會(huì)嗎? 說(shuō)不定他自己都不太理解

下面我對(duì)有關(guān)RunLoop的面試做一個(gè)簡(jiǎn)單的總結(jié),也算是對(duì)全文一個(gè)總結(jié)

  • 什么是RunLoop?

從字面上看:運(yùn)行循環(huán)、跑圈

其實(shí)它內(nèi)部就是do-while循環(huán),在這個(gè)循環(huán)內(nèi)部不斷的處理各種任務(wù)(比如Source、Timer、Observer)

一個(gè)線程對(duì)應(yīng)一個(gè)RunLoop,主線程的RunLoop默認(rèn)已經(jīng)啟動(dòng),子線程的RunLoop需要手動(dòng)啟動(dòng)(調(diào)用run方法)

RunLoop只能選擇一個(gè)Mode啟動(dòng),如果當(dāng)前Mode中沒(méi)有任何Soure、Timer、Observer,那么就直接退出RunLoop

  • 在開(kāi)發(fā)中如何使用RunLoop?什么應(yīng)用場(chǎng)景?
  • 開(kāi)啟一個(gè)常駐線程(讓一個(gè)子線程不進(jìn)入消亡狀態(tài),等待其他線程發(fā)來(lái)消息,處理其他事件)

在子線程中開(kāi)啟一個(gè)定時(shí)器

在子線程中進(jìn)行一些長(zhǎng)期監(jiān)控

  • 可以控制定時(shí)器在特定模式下執(zhí)行
  • 可以讓某些事件(行為、任務(wù))在特定模式下執(zhí)行
  • 可以添加Observer監(jiān)聽(tīng)RunLoop的狀態(tài),比如監(jiān)聽(tīng)點(diǎn)擊事件的處理(在所有點(diǎn)擊事件之前做一些事情)

***

之前發(fā)布的文章寫得不是很完整,我又花了兩天時(shí)間重新做了梳理,還有什么不足之處,歡迎大家指出,我會(huì)***時(shí)間更新.

責(zé)任編輯:龐桂玉 來(lái)源: iOS大全
相關(guān)推薦

2024-08-15 08:56:18

2011-08-15 14:27:51

CocoaRunLoop

2013-05-09 14:48:26

Windows Blu

2019-10-11 10:23:13

ClassLoaderJavaJVM

2010-05-17 09:13:35

2014-03-12 11:11:39

Storage vMo虛擬機(jī)

2021-06-07 08:18:12

云計(jì)算云端阿里云

2011-11-18 09:26:18

Javafinally

2013-03-27 10:32:53

iOS多線程原理runloop介紹GCD

2022-02-23 16:49:19

Linux內(nèi)存數(shù)據(jù)結(jié)構(gòu)

2009-06-01 09:04:44

Google WaveWeb

2018-03-01 09:33:05

軟件定義存儲(chǔ)

2010-07-05 09:07:42

2013-08-07 13:25:59

2015-08-20 13:43:17

NFV網(wǎng)絡(luò)功能虛擬化

2016-09-07 20:41:38

辦公

2016-04-06 09:27:10

runtime解密學(xué)習(xí)

2011-06-22 09:43:01

C++

2009-09-15 15:34:33

Google Fast

2023-11-02 09:55:40

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)