一:RunLoop的簡單介紹
#import "ViewController.h" @interface ViewController () @end @implementation ViewController /** * 1:Runloop和線程的關系:1:一一對應,主線程的runloop已經默認創建,但是子線程的需要手動創建:創建子線程的runloop: NSRunLoop *run = [NSRunLoop currentRunLoop];currentRunLoop懶加載的,在同一個子線程中創建多個runloop,則返回的都是同一個對象,因為其是懶加載模式的 2:在runloop中有多個運行模式,但是runloop只能選擇一種模式運行,mode里面至少要有一個timer或者是source 2:1.獲得主線程對應的runloop:NSRunLoop *mainRunLoop = [NSRunLoop mainRunLoop]; 2:獲得當前線程對應的runLoop:NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop]; 3:CFRunLoop:1:獲得主線程對應的runloop:CFRunLoopGetMain() 2:獲得當前線程對應的runLoop:CFRunLoopGetCurrent() * */ -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { //1.獲得主線程對應的runloop NSRunLoop *mainRunLoop = [NSRunLoop mainRunLoop]; //2.獲得當前線程對應的runLoop NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop]; NSLog(@"%p---%p",mainRunLoop,currentRunLoop); // NSLog(@"%@",mainRunLoop); //Core NSLog(@"%p",CFRunLoopGetMain()); NSLog(@"%p",CFRunLoopGetCurrent()); NSLog(@"%p",mainRunLoop.getCFRunLoop); //Runloop和線程的關系 //一一對應,主線程的runloop已經創建,但是子線程的需要手動創建 [[[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil] start]; } //在runloop中有多個運行模式,但是runloop只能選擇一種模式運行 //mode里面至少要有一個timer或者是source -(void)run { //如何創建子線程對應的runLoop,currentRunLoop懶加載的 NSLog(@"%@",[NSRunLoop currentRunLoop]); NSLog(@"%@",[NSRunLoop currentRunLoop]); NSLog(@"run---%@",[NSThread currentThread]); } @end
#import "ViewController.h" @interface ViewController () @end @implementation ViewController /** * 1:NSLog(@"%@",[NSRunLoop currentRunLoop]);打印當前線程的RunLoop,懶加載模式,一條線程對應一個RunLoop對象,有返回,沒有創建,主線程的RunLoop默認創建,子線程的RunLoop需要手動創建,[NSRunLoop currentRunLoop],同一個線程中若是創建多個RunLoop,則返回的都是同一個RunLoop對象,一個RunLoop里會有多個mode運行模式(系統提供了5個),但運行時只能指定一個RunLoop,若是切換RunLoop,則需要退出當前的RunLoop 2:定時器NSTimer問題:1:若是創建定時器用timerWithTimeInterval,則需要手動將定時器添加到NSRunLoop中,指定的運行模式為default,但是如果有滾動事件的時候,定時器就會停止工作。解決辦法:更改NSRunLoop的運行模式,UITrackingRunLoopMode界面追蹤,此模式是當只有發生滾動事件的時候才會開啟定時器。若是任何時候都會開啟定時器: NSRunLoopCommonModes, NSRunLoopCommonModes = NSDefaultRunLoopMode + UITrackingRunLoopMode 占用,標簽,凡是添加到NSRunLoopCommonModes中的事件愛你都會被同時添加到打上commmon標簽的運行模式上 3:1:scheduledTimerWithTimeInterval此方法創建的定時器默認加到了NSRunLoop中,並且設置運行模式為默認。 2:若是想在子線程開啟NSRunLoop:需要手動開啟:NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];等到線程銷毀的時候currentRunloop對象也隨即銷毀。2:在子線程的定時器,需要手動加入到runloop:不要忘記調用run方法 NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop]; //該方法內部自動添加到runloop中,並且設置運行模式為默認 [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES]; //開啟runloop [currentRunloop run]; /* * */ -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // NSLog(@"%@",[NSRunLoop currentRunLoop]); // [self timer2]; // [NSThread detachNewThreadSelector:@selector(timer2) toTarget:self withObject:nil]; [self timer1]; } -(void)timer1 { //1.創建定時器 NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES]; //2.添加定時器到runLoop中,指定runloop的運行模式為NSDefaultRunLoopMode /* 第一個參數:定時器 第二個參數:runloop的運行模式 */ // [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; //UITrackingRunLoopMode:界面追蹤 [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode]; // NSRunLoopCommonModes = NSDefaultRunLoopMode + UITrackingRunLoopMode //占用,標簽,凡是添加到NSRunLoopCommonModes中的事件愛你都會被同時添加到打上commmon標簽的運行模式上 /* 0 : <CFString 0x10af41270 [0x10a0457b0]>{contents = "UITrackingRunLoopMode"} 2 : <CFString 0x10a065b60 [0x10a0457b0]>{contents = "kCFRunLoopDefaultMode" */ // [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes]; } -(void)timer2 { NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop]; //該方法內部自動添加到runloop中,並且設置運行模式為默認 [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES]; //開啟runloop [currentRunloop run]; } -(void)run { NSLog(@"run-----%@---%@",[NSThread currentThread],[NSRunLoop currentRunLoop].currentMode); } @end
####1.Runloop基礎知識
- 1.1 字面意思
a 運行循環
b 跑圈
- 1.2 基本作用(作用重大)
a 保持程序的持續運行(ios程序為什么能一直活着不會死)
b 處理app中的各種事件(比如觸摸事件、定時器事件【NSTimer】、selector事件【選擇器·performSelector···】)
c 節省CPU資源,提高程序性能,有事情就做事情,沒事情就休息
- 1.3 重要說明
(1)如果沒有Runloop,那么程序一啟動就會退出,什么事情都做不了。
(2)如果有了Runloop,那么相當於在內部有一個死循環,能夠保證程序的持續運行
(2)main函數中的Runloop
a 在UIApplication函數內部就啟動了一個Runloop
該函數返回一個int類型的值
b 這個默認啟動的Runloop是跟主線程相關聯的
- 1.4 Runloop對象
(1)在iOS開發中有兩套api來訪問Runloop
a.foundation框架【NSRunloop】
b.core foundation框架【CFRunloopRef】
(2)NSRunLoop和CFRunLoopRef都代表着RunLoop對象,它們是等價的,可以互相轉換
(3)NSRunLoop是基於CFRunLoopRef的一層OC包裝,所以要了解RunLoop內部結構,需要多研究CFRunLoopRef層面的API(Core Foundation層面)
- 1.5 Runloop參考資料
```objc
(1)蘋果官方文檔
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html
(2)CFRunLoopRef開源代碼下載地址:
http://opensource.apple.com/source/CF/CF-1151.16/
```
- 1.6 Runloop與線程
1.Runloop和線程的關系:一個Runloop對應着一條唯一的線程
問題:如何讓子線程不死
回答:給這條子線程開啟一個Runloop
2.Runloop的創建:主線程Runloop已經創建好了,子線程的runloop需要手動創建
3.Runloop的生命周期:在第一次獲取時創建,在線程結束時銷毀
- 1.7 獲得Runloop對象
```objc
1.獲得當前Runloop對象
//01 NSRunloop
NSRunLoop * runloop1 = [NSRunLoop currentRunLoop];
//02 CFRunLoopRef
CFRunLoopRef runloop2 = CFRunLoopGetCurrent();
2.拿到當前應用程序的主Runloop(主線程對應的Runloop)
//01 NSRunloop
NSRunLoop * runloop1 = [NSRunLoop mainRunLoop];
//02 CFRunLoopRef
CFRunLoopRef runloop2 = CFRunLoopGetMain();
3.注意點:開一個子線程創建runloop,不是通過alloc init方法創建,而是直接通過調用currentRunLoop方法來創建,它本身是一個懶加載的。
4.在子線程中,如果不主動獲取Runloop的話,那么子線程內部是不會創建Runloop的。可以下載CFRunloopRef的源碼,搜索_CFRunloopGet0,查看代碼。
5.Runloop對象是利用字典來進行存儲,而且key是對應的線程Value為該線程對應的Runloop。
```
- 1.8 Runloop相關類
(1)Runloop運行原理圖

(2)五個相關的類
a.CFRunloopRef
b.CFRunloopModeRef【Runloop的運行模式】
c.CFRunloopSourceRef【Runloop要處理的事件源】
d.CFRunloopTimerRef【Timer事件】
e.CFRunloopObserverRef【Runloop的觀察者(監聽者)】
(3)Runloop和相關類之間的關系圖

(4)Runloop要想跑起來,它的內部必須要有一個mode,這個mode里面必須有source\observer\timer,至少要有其中的一個。
- CFRunloopModeRef
1.CFRunloopModeRef代表着Runloop的運行模式
2.一個Runloop中可以有多個mode,一個mode里面又可以有多個source\observer\timer等等
3.每次runloop啟動的時候,只能指定一個mode,這個mode被稱為該Runloop的當前mode
4.如果需要切換mode,只能先退出當前Runloop,再重新指定一個mode進入
5.這樣做主要是為了分割不同組的定時器等,讓他們相互之間不受影響
6.系統默認注冊了5個mode
a.kCFRunLoopDefaultMode:App的默認Mode,通常主線程是在這個Mode下運行
b.UITrackingRunLoopMode:界面跟蹤 Mode,用於 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其他 Mode 影響
c.UIInitializationRunLoopMode: 在剛啟動 App 時第進入的第一個 Mode,啟動完成后就不再使用
d.GSEventReceiveRunLoopMode: 接受系統事件的內部 Mode,通常用不到
e.kCFRunLoopCommonModes: 這是一個占位用的Mode,不是一種真正的Mode