你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS開發-帶你深入了解Runloop

iOS開發-帶你深入了解Runloop

編輯:IOS開發綜合

1 什麼是Runloop

從字面意思看,Runloop的意思就是
運行循環,跑圈

Runloop基本作用:

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

如果沒有Runloop

int main(int argc, char * argv[]) {
    NSLog(@"execute main function");//程序開始
    return 0;//程序結束
}

Runloop與線程

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

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

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

2 Runloop對象

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

Foundation

NSRunLoop

Core Foundation

CFRunLoopRef

NSRunLoop和CFRunLoopRef都代表著RunLoop對象

NSRunLoop是基於CFRunLoopRef的一層OC包裝,所以要了解RunLoop內部結構,需要多研究CFRunLoopRef層面的API(Core Foundation層面)

Foundation

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

3 Runloop相關的類

Core Foundation中關於RunLoop的5個類

CFRunLoopRef CFRunLoopModeRef 【Runloop的運行模式】 CFRunLoopSourceRef 【Runloop要處理的事件源】 CFRunLoopTimerRef 【Timer事件】 CFRunLoopObserverRef 【Runloop的觀察者(監聽者)】

CFRunLoopRef

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 影響

典型事例就是拖動scrollview時,NSTimer不工作,其原因就是,拖動時runloop切換到界面追蹤模式,此時其他模式的事件(例如定時器事件)就無法繼續。

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

GSEventReceiveRunLoopMode: 接受系統事件的內部 Mode,通常用不到

kCFRunLoopCommonModes: 這是一個占位用的Mode,不是一種真正的Mode

CFRunLoopSourceRef

CFRunLoopSourceRef是事件源(輸入源)

以前的分法

Port-Based Sources Custom Input Sources Cocoa Perform Selector Sources

現在的分法

Source0:非基於Port的 Source1:基於Port的

CFRunLoopTimerRef

CFRunLoopTimerRef是基於時間的觸發器

基本上說的就是NSTimer

CFRunLoopObserverRef

CFRunLoopObserverRef是觀察者,能夠監聽RunLoop的狀態改變

可以監聽的時間點有以下幾個

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),   //即將進入Runloop
    kCFRunLoopBeforeTimers = (1UL << 1),    //即將處理NSTimer
    kCFRunLoopBeforeSources = (1UL << 2),   //即將處理Sources
    kCFRunLoopBeforeWaiting = (1UL << 5),   //即將進入休眠
    kCFRunLoopAfterWaiting = (1UL << 6),    //剛從休眠中喚醒
    kCFRunLoopExit = (1UL << 7),            //即將退出runloop
    kCFRunLoopAllActivities = 0x0FFFFFFFU   //所有狀態改變
};

4 重點-Runloop運行邏輯

這裡寫圖片描述

這裡寫圖片描述

5 重點-Runloop應用

1)NSTimer
2)ImageView顯示:控制方法在特定的模式下可用
3)PerformSelector
4)常駐線程:在子線程中開啟一個runloop
5)自動釋放池的釋放時機:
第一次創建:進入runloop的時候
最後一次釋放:runloop退出的時候
其它創建和釋放:當runloop即將休眠的時候會把之前的自動釋放池釋放,然後重新創建一個新的釋放池

這裡寫圖片描述

5 重點-Runloop應用

監聽Runloop,可以在按鈕響應方法之前做些事情
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self observer];
}
-(void)observer
{
    //1.創建監聽者
    /*
     第一個參數:怎麼分配存儲空間
     第二個參數:要監聽的狀態 kCFRunLoopAllActivities 所有的狀態
     第三個參數:時候持續監聽
     第四個參數:優先級 總是傳0
     第五個參數:當狀態改變時候的回調
     */
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {

        /*
         kCFRunLoopEntry = (1UL << 0),        即將進入runloop
         kCFRunLoopBeforeTimers = (1UL << 1), 即將處理timer事件
         kCFRunLoopBeforeSources = (1UL << 2),即將處理source事件
         kCFRunLoopBeforeWaiting = (1UL << 5),即將進入睡眠
         kCFRunLoopAfterWaiting = (1UL << 6), 被喚醒
         kCFRunLoopExit = (1UL << 7),         runloop退出
         kCFRunLoopAllActivities = 0x0FFFFFFFU
         */
        switch (activity) {
            case kCFRunLoopEntry:
                NSLog(@"即將進入runloop");
                break;
            case kCFRunLoopBeforeTimers:
                NSLog(@"即將處理timer事件");
                break;
            case kCFRunLoopBeforeSources:
                NSLog(@"即將處理source事件");
                break;
            case kCFRunLoopBeforeWaiting:
                NSLog(@"即將進入睡眠");
                break;
            case kCFRunLoopAfterWaiting:
                NSLog(@"被喚醒");
                break;
            case kCFRunLoopExit:
                NSLog(@"runloop退出");
                break;

            default:
                break;
        }
    });

    /*
     第一個參數:要監聽哪個runloop
     第二個參數:觀察者
     第三個參數:運行模式
     */
    CFRunLoopAddObserver(CFRunLoopGetCurrent(),observer, kCFRunLoopDefaultMode);

    //NSDefaultRunLoopMode == kCFRunLoopDefaultMode
    //NSRunLoopCommonModes == kCFRunLoopCommonModes
}

- (IBAction)RunloopObserver:(id)sender {
    NSLog(@"處理點擊事件:%s",__func__);
}

效果圖
這裡寫圖片描述

創建一個常駐線程
- (IBAction)createThread:(id)sender {

    //1.創建線程
    self.thread = [[NSThread alloc]initWithTarget:self selector:@selector(task) object:nil];
    [self.thread start];
}
-(void)task
{
    NSLog(@"task---%@",[NSThread currentThread]);
    //    while (1) {
    //       NSLog(@"task1---%@",[NSThread currentThread]);
    //    }
    //解決方法:開runloop
    //1.獲得子線程對應的runloop
    NSRunLoop *runloop = [NSRunLoop currentRunLoop];

    //保證runloop不退出
    //NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    //[runloop addTimer:timer forMode:NSDefaultRunLoopMode];
    [runloop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];

    //2.默認是沒有開啟
    [runloop run];//開啟
//    [runloop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];//開啟Runloop,到特定時間結束

    NSLog(@"---end----");
}
- (IBAction)doOthertask:(id)sender {
    //[self.thread start];

    [self performSelector:@selector(OtheTask) onThread:self.thread withObject:nil waitUntilDone:YES];
}
-(void)OtheTask
{
    NSLog(@"OtheTask---%@",[NSThread currentThread]);
}

-(void)run
{
    NSLog(@"%s",__func__);
}

//Runloop中自動釋放池的創建和釋放
//第一次創建:啟動runloop
//最後一次銷毀:runloop退出的時候
//其他時候的創建和銷毀:當runloop即將睡眠的時候銷毀之前的釋放池,重新創建一個新的

效果圖
這裡寫圖片描述

Runloop源碼分析
這裡寫圖片描述

 

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved