iOS開(kāi)發(fā)-RunLoop

RunLoop

從字面意思來(lái)看:跑圈、運(yùn)動(dòng)循環(huán)
基本用法:保持程序持續(xù)運(yùn)行、處理App中的各種事件(觸摸事件、定時(shí)器事件、SEL等等)
為什么需要它:節(jié)省CPU資源、 提高性能

如果沒(méi)有RunLoop,程序在執(zhí)行到7行就結(jié)束了。

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
    }
    return 0;
}

有Runloop后,程序就相當(dāng)于一直在做循環(huán)

在這個(gè)循環(huán)內(nèi)部不斷地處理各種任務(wù)(比如Source、Timer、Observer)

int main(int argc, const char * argv[]) {
   
    BOOL running = YES;
    do{
        // 執(zhí)行各種任務(wù),處理各種時(shí)間
        
    }while (running);
    
    return 0;
}

程序中的Runloop----UIApplicationMain

int main(int argc, char *argv[]) 
{
       @autoreleasepool { 
              return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 
       }
 }

1.在UIApplicationMain函數(shù)內(nèi)部就啟動(dòng)了一個(gè)RunLoop
2.UIApplicationMain函數(shù)一直沒(méi)有返回,保持了程序的持續(xù)運(yùn)行
3.這個(gè)默認(rèn)啟動(dòng)的RunLoop是主線程關(guān)聯(lián)的。
4.一個(gè)線程對(duì)應(yīng)一個(gè)RunLoop,主線程的RunLoop默認(rèn)已經(jīng)啟動(dòng)
5.子線程的RunLoop得手動(dòng)啟動(dòng)(調(diào)用run方法)
6.RunLoop只能選擇一個(gè)Mode啟動(dòng),如果當(dāng)前Mode中沒(méi)有任Source、Timer、Observer,那么就直接退出RunLoop

RunLoop里面有兩套api用來(lái)訪問(wèn)和使用RunLoop
1、Foundation--NSRunLoop
2、Core Foundation --- CFRunloopRef
二者異同點(diǎn):
NSRunLoop和CFRunloopRef都代表RunLoop對(duì)象,NSRunLoop是對(duì)CFRunloopRef一層OC的封裝

RunLoop與線程:

每條線程都有一個(gè)RunLoop對(duì)象,主線程默認(rèn)已經(jīng)創(chuàng)建好了,子線程需要主動(dòng)創(chuàng)建
Runloop在第一次獲取時(shí)創(chuàng)建,在線程結(jié)束后銷毀。

   /* 1. Foundation  */
    // 獲取當(dāng)前線程
    NSRunLoop *roop = [NSRunLoop currentRunLoop];
    // 獲取主線程
    [NSRunLoop mainRunLoop];

   /*2. Core Foundation  */
    // 獲取當(dāng)期線程
    CFRunLoopGetCurrent();
    // 獲取主線程
    CFRunLoopGetMain();

RunLoop相關(guān)類(Runloop中如果沒(méi)有Source,Observre,Timer,Mode,就會(huì)結(jié)束)

    // Runloop對(duì)象
    CFRunLoopRef;
 
    // Runloop事件源(數(shù)據(jù)源)
    CFRunLoopSourceRef;
    
    // Runloop觀察者
    CFRunLoopObserverRef;
    
    // Runloop時(shí)間源
    CFRunLoopTimerRef;

    // Runloop模式
    CFRunloopModeRef;

Runloop里面相關(guān)類的互相關(guān)系-

Paste_Image.png
    // 獲取當(dāng)前Runloop的模式
    NSString *runloopMode = [NSRunLoop currentRunLoop].currentMode;

1.同一時(shí)間只可以運(yùn)行其中的一種model
2.切換Model只能退出Runloop,重新進(jìn)入
3.系統(tǒng)默認(rèn)注冊(cè)了5個(gè)Mode

//App默認(rèn)的Mode,通常主線程是在這個(gè)Mode下運(yùn)行
NSDefaultRunLoopMode:
//界面追蹤Mode,用于ScrollView追蹤觸摸滑動(dòng),保證界面滑動(dòng)時(shí)不受其他Mode影響
UITrackingRunLoopMode
//這是一個(gè)占位用的Mode,不是一種正真的Mode
NSRunLoopCommonModes

//在剛啟動(dòng)App進(jìn)入的第一個(gè)Mode,啟動(dòng)完成以后就不再使用
UIInitializationRunLoopMode
//接收系統(tǒng)事件的內(nèi)部Mode,通常用不到
GSEventReceiveRunLoopMode

CFRunLoopTimerRef

CFRunLoopTimerRef是基于時(shí)間的觸發(fā)器

CFRunLoopTimerRef基本上說(shuō)就是NSTimer,它受RunLoop的Mode影響

NSTimer *time = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(test) userInfo:nil repeats:YES];
// 定時(shí)器只運(yùn)行在NSDefaultRunLoopMode下,一旦RunLoop進(jìn)入其他模式,這個(gè)定時(shí)器就不會(huì)工作
[[NSRunLoop currentRunLoop] addTimer:time forMode:NSDefaultRunLoopMode];
    
// 定時(shí)器會(huì)跑在標(biāo)記為common modes的模式下
// 標(biāo)記為common modes的模式:包含:UITrackingRunLoopMode和kCFRunLoopDefaultMode
[[NSRunLoop currentRunLoop] addTimer:time forMode:NSRunLoopCommonModes];

GCD不受RunLoop的Mode影響

    // 獲取一個(gè)全局并發(fā)隊(duì)列
    /*
     #define DISPATCH_QUEUE_PRIORITY_HIGH 2
     #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0  默認(rèn)
     #define DISPATCH_QUEUE_PRIORITY_LOW (-2)
     #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
     */
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 1. 設(shè)置定時(shí)器
    // dispatchQueue :決定了將來(lái)回調(diào)的方法在哪里執(zhí)行。
    // dispatch_source_t timer  是一個(gè)OC對(duì)象
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    
    // 2.指定定時(shí)期開(kāi)始的時(shí)間和間隔的時(shí)間
    // DISPATCH_TIME_NOW :第2個(gè)參數(shù):定時(shí)器開(kāi)始時(shí)間   (也可以抽出來(lái),設(shè)置間隔時(shí)間)
        dispatch_time_t startTime = dispatch_time(DISPATCH_TIME_NOW, 1.0 *NSEC_PER_SEC);
    
    // intervalInSeconds :第三個(gè)參數(shù):定時(shí)器開(kāi)始后的間隔時(shí)間
    // leewayInSeconds:第四個(gè)參數(shù):間隔精準(zhǔn)度,0代標(biāo)最精準(zhǔn),傳入一個(gè)大于0的數(shù),代表多少秒的范圍是可以接收的,主要為了提高程序性能,積攢一定的時(shí)間,Runloop執(zhí)行完任務(wù)會(huì)睡覺(jué),這個(gè)方法讓他多睡一會(huì),積攢時(shí)間,任務(wù)也就相應(yīng)多了一點(diǎn),而后一起執(zhí)行
    // 開(kāi)始時(shí)間

    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 3.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
    
    // 3.指定定時(shí)器回調(diào)方法
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"___________");
    });
    // 開(kāi)啟定時(shí)器
    dispatch_resume(timer);

CFRunLoopSourceRef(事件源、輸入源)

Port-Based Sources (端口)
Custom Input (自定義事件)
Cocoa Perform Selector Sources 

按照函數(shù)的調(diào)用棧
Source0:非基于Port的
Source1:基于Port 通過(guò)內(nèi)核和其他線程通信,接收分發(fā)系統(tǒng)事件

CFRunLoopObserverRef(觀察者)

能夠監(jiān)聽(tīng)RunLoop的狀態(tài)改變


Paste_Image.png

CFRunLoopObserverRef

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),      // 1
    kCFRunLoopBeforeTimers = (1UL << 1), // 2
    kCFRunLoopBeforeSources = (1UL << 2), // 4
    kCFRunLoopBeforeWaiting = (1UL << 5), // 32
    kCFRunLoopAfterWaiting = (1UL << 6), // 64
    kCFRunLoopExit = (1UL << 7),       // 128
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

創(chuàng)建一個(gè)Observer

 /*
     第1個(gè)參數(shù):如何給Observer分配存儲(chǔ)空間
     第2個(gè)參數(shù):需要監(jiān)聽(tīng)的狀態(tài)類型/kCFRunLoopAllActivities監(jiān)聽(tīng)所有狀態(tài)
     第3個(gè)參數(shù):是否每次需要監(jiān)聽(tīng)?
     第4個(gè)參數(shù):優(yōu)先級(jí)(0)
     第5個(gè)參數(shù):監(jiān)聽(tīng)狀態(tài)改變后的回調(diào)
     */
    
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        switch (activity) {
            case kCFRunLoopEntry:
                NSLog(@"即將進(jìn)入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;
        }
    });
    /*
    第1個(gè)參數(shù):要給那個(gè)RunLoop添加觀察者
    第2個(gè)參數(shù):添加的Observer對(duì)象
    第3個(gè)參數(shù):在那種模式下監(jiān)聽(tīng)
     */
    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopDefaultMode);

    //  釋放對(duì)象
    CFRelease(observer);

由于ARC只管理Foundation框架的內(nèi)容,所以我們?cè)贑ore Foundation 框架創(chuàng)建的對(duì)象必須手動(dòng)釋放。
規(guī)律:
凡是帶有copy、create、retain等字眼的函數(shù),創(chuàng)建出來(lái)的CF對(duì)象,都需要在最后做一次release

官方對(duì)于RunLoop的解釋:

Paste_Image.png

RunLoop處理邏輯,整理:自動(dòng)釋放池的生命周期

RunLoop在進(jìn)入這個(gè) kCFRunLoopBeforeWaiting時(shí),會(huì)對(duì)自動(dòng)釋放池銷毀


Paste_Image.png

Runloop:在開(kāi)發(fā)中有什么作用?

1.NSTimer
2.ImageView的顯示
3.PerformSelector
4.常駐線程
5.自動(dòng)釋放池

PerformSelector

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{  
  /*
     - (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSString *> *)modes;
     */
    // 指定模式下進(jìn)行特定的操作
    [self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"rightPic"] afterDelay:2.0 inModes:@[NSDefaultRunLoopMode]];
}

常駐線程:
默認(rèn)情況下,一個(gè)線程只能使用一次,也就是只能執(zhí)行一個(gè)操作
我們需要做得到就是強(qiáng)引用,保留線程,同時(shí)添加Source Or Timer,注:系統(tǒng)只會(huì)監(jiān)測(cè)Source Or Timer,不會(huì)檢查Observer

1.子線程的Runloop需要手動(dòng)創(chuàng)建
2.子線程的Runloop需要手動(dòng)開(kāi)啟
3.如果子線程的NSRunLoop沒(méi)有設(shè)置Source Or Timer 那么子線程會(huì)立刻關(guān)閉。

- (void)ViewDidLoad
{
    [super viewDidLoad];
    SYThread *thread = [[SYThread alloc] initWithTarget:self selector:@selector(show) object:nil];
    self.thread = thread;
    [thread start];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self performSelector:@selector(test) onThread:self.thread withObject:@"時(shí)間計(jì)算" waitUntilDone:YES modes:@[NSRunLoopCommonModes]];
}

- (void)show
{
    NSLog(@"%s", __func__);
    // 這句話并沒(méi)有意義,只是保證線程不死
    [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
    [[NSRunLoop currentRunLoop] run];
    // 上面線程不死,這句話永遠(yuǎn)不會(huì)打印
    NSLog(@"-------$$$$");
}
- (void)test
{
    NSLog(@"11111");
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容