iOS開發RunLoop學習:一:RunLoop簡單介紹


一: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運行原理圖

 

![PNG](2.png)

 

(2)五個相關的類

 

a.CFRunloopRef

b.CFRunloopModeRef【Runloop的運行模式】

c.CFRunloopSourceRef【Runloop要處理的事件源】

d.CFRunloopTimerRef【Timer事件】

e.CFRunloopObserverRef【Runloop的觀察者(監聽者)】

 

(3)Runloop和相關類之間的關系圖

 

 ![PNG](1.png)

 

(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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM