美文网首页
07进阶之路-RunLoop

07进阶之路-RunLoop

作者: 进击的iOS开发 | 来源:发表于2018-06-04 17:20 被阅读0次

    1. RunLoop本质

    学习链接
    RunLoop是通过内部维护的事件循环来对事件、消息进行管理的一个对象
    事件循环:没有消息需要处理时,休眠以避免资源占用。有消息处理时,立刻被唤醒。
    休眠时:用户态转为内核态
    唤醒时:内核态转为用户态

    image

    2. RunLoop的数据结构

    CFRunLoop里面包括:
    pthread -->一一对应(一个线程只有一个block)
    currentMode --> CFRunloopMode
    modes --> NSMutableSet<CFRunloopMode *>
    commonModes --> NSMutableSet<NSString *>
    commonModeItems --> 数组,包括多个Oberver,Timer,Source


    CFRunloopModel里面包括:
    name -->kCFRunLoopDefaultMode
    sources0 --> 需要手动唤醒线程
    sources1 --> 具备唤醒线程的能力
    observers --> NSMutableArray

    typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
        kCFRunLoopEntry         = (1UL << 0), // 即将进入Loop
        kCFRunLoopBeforeTimers  = (1UL << 1), // 即将处理 Timer
        kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
        kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
        kCFRunLoopAfterWaiting  = (1UL << 6), // 刚从休眠中唤醒
        kCFRunLoopExit          = (1UL << 7), // 即将退出Loop
    };
    

    timers --> NSMutableArray
    系统默认注册了5个Mode:

    1. kCFRunLoopDefaultMode: App的默认 Mode,通常主线程是在这个 Mode 下运行的。
    2. UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响。
    3. UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用。
    4. GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到。
    5. kCFRunLoopCommonModes: 这是一个占位的 Mode,没有实际作用。

    CommonMode的特殊性
    NSRunLoopCommonModes

    • CommonMode不是实际存在的一种 mode
    • 是同步Source/Timer/Obervre到多个mode的一种技术方案


      image

    3. RunLoop事件循环机制

    image

    4. RunLoop与NSTimer

    这里有个概念叫 “CommonModes”:一个 Mode 可以将自己标记为”Common”属性(通过将其 ModeName 添加到 RunLoop 的 “commonModes” 中)。每当 RunLoop 的内容发生变化时,RunLoop 都会自动将 _commonModeItems 里的 Source/Observer/Timer 同步到具有 “Common” 标记的所有Mode里。

    应用场景举例:主线程的 RunLoop 里有两个预置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。这两个 Mode 都已经被标记为”Common”属性。DefaultMode 是 App 平时所处的状态,TrackingRunLoopMode 是追踪 ScrollView 滑动时的状态。当你创建一个 Timer 并加到 DefaultMode 时,Timer 会得到重复回调,但此时滑动一个TableView时,RunLoop 会将 mode 切换为 TrackingRunLoopMode,这时 Timer 就不会被回调,并且也不会影响到滑动操作。

    有时你需要一个 Timer,在两个 Mode 中都能得到回调,一种办法就是将这个 Timer 分别加入这两个 Mode。还有一种方式,就是将 Timer 加入到顶层的 RunLoop 的 “commonModeItems” 中。”commonModeItems” 被 RunLoop 自动更新到所有具有”Common”属性的 Mode 里去。

    5. RunLoop与多线程和常驻线程

    • 线程和RunLoop一一对用
    • 子线程默认没有开启,需要手动获取,系统会自动创建
    • 实现一个常驻线程
    1. 为当前线程开启一个RunLoop
    2. 向该RunLoop添加一个Port/Source等维护RunLoop的事件循环
    3. 启动该RunLoop
    @implementation MCObject
    
    static NSThread *thread = nil;
    // 标记是否要继续事件循环
    static BOOL runAlways = YES;
    
    + (NSThread *)threadForDispatch{
        if (thread == nil) {
            @synchronized(self) {
                if (thread == nil) {
                    // 线程的创建
                    thread = [[NSThread alloc] initWithTarget:self selector:@selector(runRequest) object:nil];
                    [thread setName:@"com.imooc.thread"];
                    //启动
                    [thread start];
                }
            }
        }
        return thread;
    }
    
    + (void)runRequest
    {
        // 创建一个Source
        CFRunLoopSourceContext context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
        CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
        
        // 创建RunLoop,同时向RunLoop的DefaultMode下面添加Source
        CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
        
        // 如果可以运行
        while (runAlways) {
            @autoreleasepool {
                // 令当前RunLoop运行在DefaultMode下面
                CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, true);
            }
        }
        
        // 某一时机 静态变量runAlways = NO时 可以保证跳出RunLoop,线程退出
        CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
        CFRelease(source);
    }
    
    @end
    
    

    相关文章

      网友评论

          本文标题:07进阶之路-RunLoop

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