Run Loop

作者: 大白菜s | 来源:发表于2019-08-15 11:00 被阅读0次

运行循环,在程序运行中循环做事情,如果没有runloop程序一运行就会马上退出

基本作用

保持程序的持续运行
处理App中的各种事件(比如触摸事件、定时器事件等)
节省CPU资源,提高程序性能:该做事时做事,该休息时休息

iOS中有2套API来访问和使用RunLoop

Foundation:NSRunLoop
Core Foundation:CFRunLoopRef
NSRunLoop和CFRunLoopRef都代表着RunLoop对象
NSRunLoop是基于CFRunLoopRef的一层OC包装
CFRunLoopRef是开源的 https://opensource.apple.com/tarballs/CF/

RunLoop与线程

每条线程都有唯一的一个与之对应的RunLoop对象
RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建
RunLoop会在线程结束时销毁
主线程的RunLoop已经自动获取(创建)
子线程默认没有开启RunLoop

   CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));//线程为key,__CFRunLoops(字典)中取
    __CFUnlock(&loopsLock);
    if (!loop) {
    CFRunLoopRef newLoop = __CFRunLoopCreate(t);//创建runloop
        __CFLock(&loopsLock);
    loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));//线程为key,runloop为value,存入字典
    if (!loop) {
        CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
        loop = newLoop;
    }

Core Foundation中关于RunLoop的5个类

  • CFRunLoopRef - 获得当前RunLoop和主RunLoop
  • CFRunLoopModeRef - RunLoop 运行模式,只能选择一种,在不同模式中做不同的操作
  • CFRunLoopSourceRef - 事件源,输入源
  • CFRunLoopTimerRef - 定时器时间
  • CFRunLoopObserverRef - 观察者
CFRunLoopRef

CFRunLoopModeRef代表RunLoop的运行模式

CFRunLoopModeRef

一个RunLoop包含若干个Mode,每个Mode又包含若干个Source0/Source1/Timer/Observer
RunLoop启动时只能选择其中一个Mode,作为currentMode
如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入
不同组的Source0/Source1/Timer/Observer能分隔开来,互不影响
如果Mode里没有任何Source0/Source1/Timer/Observer,RunLoop会立马退出

常见的2种Mode

kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默认Mode,通常主线程是在这个Mode下运行
UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响

有什么事件就处理什么事件,没有就休眠

  • Source0 触摸事件处理 performSelector:onThread:
  • Source1 基于Port的线程间通信 系统事件捕捉
  • Timers NSTimer performSelector:withObject:afterDelay:
  • Observers 用于监听RunLoop的状态 UI刷新(BeforeWaiting) Autorelease pool(BeforeWaiting)
CFRunLoopObserverRef 观察者,能够监听RunLoop的状态改变

我们直接来看代码,给RunLoop添加监听者,监听其运行状态

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
     //创建监听者
     /*
     第一个参数 CFAllocatorRef allocator:分配存储空间 CFAllocatorGetDefault()默认分配
     第二个参数 CFOptionFlags activities:要监听的状态 kCFRunLoopAllActivities 监听所有状态
     第三个参数 Boolean repeats:YES:持续监听 NO:不持续
     第四个参数 CFIndex order:优先级,一般填0即可
     第五个参数 :回调 两个参数observer:监听者 activity:监听的事件
     */
     /*
     所有事件
     typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
     kCFRunLoopEntry = (1UL << 0),   //   即将进入RunLoop
     kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理Timer
     kCFRunLoopBeforeSources = (1UL << 2), // 即将处理Source
     kCFRunLoopBeforeWaiting = (1UL << 5), //即将进入休眠
     kCFRunLoopAfterWaiting = (1UL << 6),// 刚从休眠中唤醒
     kCFRunLoopExit = (1UL << 7),// 即将退出RunLoop
     kCFRunLoopAllActivities = 0x0FFFFFFFU
     };
     */
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        switch (activity) {
            case kCFRunLoopEntry:
                NSLog(@"RunLoop进入");
                break;
            case kCFRunLoopBeforeTimers:
                NSLog(@"RunLoop要处理Timers了");
                break;
            case kCFRunLoopBeforeSources:
                NSLog(@"RunLoop要处理Sources了");
                break;
            case kCFRunLoopBeforeWaiting:
                NSLog(@"RunLoop要休息了");
                break;
            case kCFRunLoopAfterWaiting:
                NSLog(@"RunLoop醒来了");
                break;
            case kCFRunLoopExit:
                NSLog(@"RunLoop退出了");
                break;
                
            default:
                break;
        }
    });
    
    // 给RunLoop添加监听者
    /*
     第一个参数 CFRunLoopRef rl:要监听哪个RunLoop,这里监听的是主线程的RunLoop
     第二个参数 CFRunLoopObserverRef observer 监听者
     第三个参数 CFStringRef mode 要监听RunLoop在哪种运行模式下的状态
     */
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
     /*
     CF的内存管理(Core Foundation)
     凡是带有Create、Copy、Retain等字眼的函数,创建出来的对象,都需要在最后做一次release
     GCD本来在iOS6.0之前也是需要我们释放的,6.0之后GCD已经纳入到了ARC中,所以我们不需要管了
     */
    CFRelease(observer);
}
RunLoop的运行逻辑

01、通知Observers:进入Loop
02、通知Observers:即将处理Timers
03、通知Observers:即将处理Sources
04、处理Blocks
05、处理Source0(可能会再次处理Blocks)
06、如果存在Source1,就跳转到第8步
07、通知Observers:开始休眠(等待消息唤醒)
08、通知Observers:结束休眠(被某个消息唤醒)
01> 处理Timer
02> 处理GCD Async To Main Queue
03> 处理Source1
09、处理Blocks
10、根据前面的执行结果,决定如何操作
01> 回到第02步
02> 退出Loop
11、通知Observers:退出Loop

相关文章

  • runloop

    走进Run Loop的世界 (一):什么是Run Loop?走进Run Loop的世界 (二):如何配置Run L...

  • iOS Runloop学习笔记

    一、** what is run loop ** 1、A run loop is an abstraction t...

  • iOS开发之Run loop

    1.什么是Run loop,Run loop有什么作用? 2.Run loop 是怎么运作的? 3.什么情况下使用...

  • 详解Run Loop

    Run Loop Run Loop是事件驱动的。 iOS中有2套API来访问使用Run LoopFoundatio...

  • iOS Runloop(二)

    Run Loop观察者源是合适的同步或异步事件发生时触发,而run loop观察者则是在run loop本身运行的...

  • python asyncio并发编程(3)

    1. loop.run_forever()与loop.run_until_complete()的区别 (1) l...

  • IOS多线程编程指南二之Runloop

    一、什么是Runloop A run loop is an event processing loop that ...

  • Run Loops基础概念篇二

    When Would You Use a Run Loop? 你唯一要使用run loop,就是当你要在appli...

  • CFRunLoop -- API

    获取 Run Loop 的方法 运行以及停止 Run Loop 的方法 管理 Sources 的方法 管理 Obs...

  • Run Loop

    run loop(运行循环)是与线程相关的基础架构的一部分。它是一个事件处理循环,用于调度工作和协调传入事件的接收...

网友评论

      本文标题:Run Loop

      本文链接:https://www.haomeiwen.com/subject/ctinjctx.html