成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

解密-神秘的RunLoop

移動開發 iOS
一直以來RunLoop就是個神秘的領域,好多2.3年的開發者都不能準確的表述它的作用,說它神秘,其實RunLoop并沒有大家想象中的那么神秘,那么不好理解,本文就帶大家好好剖析一下”神秘的RunLoop”。

引言

一直以來RunLoop就是個神秘的領域,好多2.3年的開發者都不能準確的表述它的作用,說它神秘,其實RunLoop并沒有大家想象中的那么神秘,那么不好理解,本文就帶大家好好剖析一下”神秘的RunLoop”

什么是RunLoop

從字面上看

  • 運行循環
  • 跑圈 

 

 

[[184147]] 

循環

基本作用

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

存在價值 

 

 

 

沒有RunLoop 

 

 

 

有RunLoop 

 

 

 

主運行循環

  • 第14行代碼的UIApplicationMain函數內部就啟動了一個RunLoop
  • 所以UIApplicationMain函數一直沒有返回,保持了程序的持續運行
  • 這個默認啟動的RunLoop是跟主線程相關聯的

RunLoop對象

  • iOS中有2套API來訪問和使用RunLoop

Foundation

NSRunLoop

Core Foundation

CFRunLoopRef

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

RunLoop資料

  • 蘋果官方文檔

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

  • CFRunLoopRef是開源的

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

RunLoop與線程

每條線程都有唯一的一個與之對應的RunLoop對象

主線程的RunLoop已經自動創建好了,子線程的RunLoop需要主動創建

RunLoop在***次獲取時創建,在線程結束時銷毀

獲取RunLoop對象

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

RunLoop相關類

  • Core Foundation中關于RunLoop的5個類

CFRunLoopRef

CFRunLoopModeRef

CFRunLoopSourceRef

CFRunLoopTimerRef

CFRunLoopObserverRef

注:RunLoop如果沒有這些東西 會直接退出

CFRunLoopModeRef

  • CFRunLoopModeRef代表RunLoop的運行模式

一個 RunLoop 包含若干個 Mode,每個Mode又包含若干個Source/Timer/Observer

每次RunLoop啟動時,只能指定其中一個 Mode,這個Mode被稱作 CurrentMode

如果需要切換Mode,只能退出Loop,再重新指定一個Mode進入

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

 

 

相關類 

相關類

系統默認注冊了5個Mode:(前兩個跟***一個常用)

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

CFRunLoopSourceRef

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

Port-Based Sources (基于端口,跟其他線程交互,通過內核發布的消息)

Custom Input Sources (自定義)

Cocoa Perform Selector Sources (performSelector…方法)

  • 按照函數調用棧的分類

Source0:非基于Port的

Source1:基于Port的

Source0: event事件,只含有回調,需要先調用CFRunLoopSourceSignal(source),將這個 Source 標記為待處理,然后手動調用 CFRunLoopWakeUp(runloop) 來喚醒 RunLoop。

Source1: 包含了一個 mach_port 和一個回調,被用于通過內核和其他線程相互發送消息,能主動喚醒 RunLoop 的線程。

函數調用棧 

 

 

函數調用棧 

函數調用棧

CFRunLoopTimerRef

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

CFRunLoopObserverRef

  • CFRunLoopObserverRef是觀察者,能夠監聽RunLoop的狀態改變
  • 可以監聽的時間點有以下幾個

使用

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

 特別注意

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

RunLoop處理邏輯

– 官方版 

 

 

  

 

 

 

邏輯

– 網友整理版 

 

 

 

網友版

注:進入RunLoop前 會判斷模式是否為空,為空直接退出

RunLoop應用

  • NSTimer
  • ImageView顯示
  • PerformSelector
  • 常駐線程
  • 自動釋放池

1.NSTimer(最常見RunLoop使用)

  1. - (void)timer 
  2.  
  3.  
  4.     NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES]; 
  5.  
  6.     // 定時器只運行在NSDefaultRunLoopMode下,一旦RunLoop進入其他模式,這個定時器就不會工作 
  7.  
  8.     //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; 
  9.  
  10.   
  11.  
  12.     // 定時器只運行在UITrackingRunLoopMode下,一旦RunLoop進入其他模式,這個定時器就不會工作 
  13.  
  14.     //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode]; 
  15.  
  16.   
  17.  
  18.     // 定時器會跑在標記為common modes的模式下 
  19.  
  20.     // 標記為common modes的模式:UITrackingRunLoopMode和NSDefaultRunLoopMode兼容 
  21.  
  22.     [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; 
  23.  
  24.   
  1. - (void)timer2 
  2.  
  3.  
  4.     // 調用了scheduledTimer返回的定時器,已經自動被添加到當前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.  

場景還原

拖拽時模式由NSDefaultRunLoopMode 進入 UITrackingRunLoopMode

此時如下圖: NSTimer 不再響應 圖片停止輪播 

 

 

 

NSDefaultRunLoopMode模式

NSRunLoopCommonModes 模式下兩種模式都可運行

此時如下圖: NSTimer 在兩個模式下都可正常運行 

 

 

 

2.ImageView

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

方法1 監聽UIScrollerView滾動 (通過UIScrollViewDelegate監聽,此處不再舉例)

方法2 RunLoop 設置運行模式

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

 

 

3.PerformSelector

 

inModes:設置運行模式

4.常駐線程 (重要)

應用場景:經常在后臺進行耗時操作,如:監控聯網狀態,掃描沙盒等 不希望線程處理完事件就銷毀,保持常駐狀態

***種(推薦)

開啟

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

退出-退出當前線程

  1. [NSThread exit]; 

第二種(奇葩法)

優點:退出RunLoop比較方便-定義個標記 while(flag){…}

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

5.自動釋放池 

 

 

 

在休眠前(kCFRunLoopBeforeWaiting)進行釋放,處理事件前創建釋放池,中間創建的對象會放入釋放池

特別注意:

在啟動RunLoop之前建議用 @autoreleasepool {…}包裹

意義:創建一個大釋放池,釋放{}期間創建的臨時對象,一般好的框架的作者都會這么做

  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.  

題外話:

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

補充:GCD定時器

一般的NSTimer定時器因為受到RunLoop,會存在時間不準時的情況.

上文有提到GCD不受RunLoop影響,下面簡單的說一下它的使用

  1. /** 定時器(這里不用帶*,因為dispatch_source_t就是個類,內部已經包含了*) */ 
  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.     // 獲得隊列 
  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.     // 創建一個定時器(dispatch_source_t本質還是個OC對象) 
  21.  
  22.     self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); 
  23.  
  24.   
  25.  
  26.     // 設置定時器的各種屬性(幾時開始任務,每隔多長時間執行一次) 
  27.  
  28.     // GCD的時間參數,一般是納秒 NSEC_PER_SEC(1秒 == 10的9次方納秒) 
  29.  
  30.     // 何時開始執行***個任務 
  31.  
  32.     // dispatch_time(DISPATCH_TIME_NOW, 3.0 * NSEC_PER_SEC) 比當前時間晚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.     // 設置回調 
  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. //            // 取消定時器 
  55.  
  56. //            dispatch_cancel(self.timer); 
  57.  
  58. //            self.timer = nil; 
  59.  
  60. //        } 
  61.  
  62.     }); 
  63.  
  64.   
  65.  
  66.     // 啟動定時器 
  67.  
  68.     dispatch_resume(self.timer); 
  69.  
  70.  

RunLoop面試題

經常會有喜歡裝B的面試官,面試的時候就喜歡問RunLoop,其實他真的會嗎? 說不定他自己都不太理解

下面我對有關RunLoop的面試做一個簡單的總結,也算是對全文一個總結

  • 什么是RunLoop?

從字面上看:運行循環、跑圈

其實它內部就是do-while循環,在這個循環內部不斷的處理各種任務(比如Source、Timer、Observer)

一個線程對應一個RunLoop,主線程的RunLoop默認已經啟動,子線程的RunLoop需要手動啟動(調用run方法)

RunLoop只能選擇一個Mode啟動,如果當前Mode中沒有任何Soure、Timer、Observer,那么就直接退出RunLoop

  • 在開發中如何使用RunLoop?什么應用場景?
  • 開啟一個常駐線程(讓一個子線程不進入消亡狀態,等待其他線程發來消息,處理其他事件)

在子線程中開啟一個定時器

在子線程中進行一些長期監控

  • 可以控制定時器在特定模式下執行
  • 可以讓某些事件(行為、任務)在特定模式下執行
  • 可以添加Observer監聽RunLoop的狀態,比如監聽點擊事件的處理(在所有點擊事件之前做一些事情)

***

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

責任編輯:龐桂玉 來源: iOS大全
相關推薦

2024-08-15 08:56:18

2013-05-09 14:48:26

Windows Blu

2011-08-15 14:27:51

CocoaRunLoop

2010-05-17 09:13:35

2011-11-18 09:26:18

Javafinally

2021-06-07 08:18:12

云計算云端阿里云

2014-03-12 11:11:39

Storage vMo虛擬機

2019-10-11 10:23:13

ClassLoaderJavaJVM

2015-08-20 13:43:17

NFV網絡功能虛擬化

2009-09-15 15:34:33

Google Fast

2016-04-06 09:27:10

runtime解密學習

2023-11-02 09:55:40

2016-09-07 20:41:38

辦公

2011-06-22 09:43:01

C++

2023-04-10 11:00:00

注解Demo源碼

2009-06-01 09:04:44

Google WaveWeb

2010-07-05 09:07:42

2018-03-01 09:33:05

軟件定義存儲

2013-08-07 13:25:59

2022-02-23 16:49:19

Linux內存數據結構
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 这里只有精品999 | 国产福利在线视频 | 久久精品这里 | 999www视频免费观看 | 91色网站 | 精品久久一区 | 久久久毛片| 成人免费日韩 | 天天拍天天操 | 日一区二区 | 国产精品亚洲欧美日韩一区在线 | 亚洲先锋影音 | 久久综合影院 | 久草精品视频 | 看片91| 国产电影一区二区在线观看 | 蜜桃av人人夜夜澡人人爽 | 国产精品视频97 | www.成人.com | 欧美成人猛片aaaaaaa | 亚洲一二三区在线观看 | aaaa网站 | 国产精品久久久久久久久久三级 | 网站黄色av| 男人天堂视频在线观看 | 亚洲va国产日韩欧美精品色婷婷 | 日韩在线不卡视频 | 亚洲欧美日韩国产综合 | 美女毛片免费看 | 久久久久久免费精品一区二区三区 | 欧美精品一区二区三区在线四季 | 在线观看www视频 | 欧美成人h版在线观看 | 亚洲精品美女视频 | 精品一区在线 | 成人在线免费电影 | 97久久精品午夜一区二区 | 国产不卡在线 | 99久久免费精品视频 | 中文字幕爱爱视频 | 久久国产区 |