zoukankan      html  css  js  c++  java
  • NSRunLoop

    iOS系统提供了两个对象:NSRunLoop 和 CFRunLoopRef。NSRunLoop 是基于 CFRunLoopRef 的封装。

    苹果不允许直接创建 RunLoop,它只提供了两个自动获取的函数:CFRunLoopGetMain() 和 CFRunLoopGetCurrent()。这两个函数内部的逻辑大概是下面这样:

    /// 全局的Dictionary,key 是 pthread_t, value 是 CFRunLoopRef
    static CFMutableDictionaryRef loopsDic;
    static CFSpinLock_t loopsLock;
    
    /// 获取一个 pthread 对应的 RunLoop。
    CFRunLoopRef _CFRunLoopGet(pthread_t thread) {
        
        OSSpinLockLock(&loopsLock);
        
        if (!loopsDic) {
        
            loopsDic = CFDictionaryCreateMutable();
            CFRunLoopRef mainLoop = _CFRunLoopCreate();
            CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop);
        }
        
        CFRunLoopRef loop = CFDictionaryGetValue(loopsDic, thread));
        
        if (!loop) {
    
            loop = _CFRunLoopCreate();
            CFDictionarySetValue(loopsDic, thread, loop);
            /// 注册一个回调,当线程销毁时,顺便也销毁其对应的 RunLoop。
            _CFSetTSD(..., thread, loop, __CFFinalizeRunLoop);
        }
        
        OSSpinLockUnLock(&loopsLock);
        
        return loop;
    }
    
    CFRunLoopRef CFRunLoopGetMain() {
        return _CFRunLoopGet(pthread_main_thread_np());
    }
    
    CFRunLoopRef CFRunLoopGetCurrent() {
        return _CFRunLoopGet(pthread_self());
    }

      从上面的代码可以看出,线程和RunLoop之间是一一对应的,其关系保存在一个全局的Dictionary里。线程刚创建时并没有RunLoop,如果你不主动获取,那它一直都不会有。RunLoop的创建发生在第一次获取时,RunLoop的销毁发生在线程结束时。

      线程获取RunLoop后还需要传入一种运行模式(Mode),让其跑起来。

    系统默认注册了5种Mode:

    1. kCFRunLoopDefaultMode,默认模式。
    2. UITrackingRunLoopDefaultMode:界面跟踪Mode,用于scrollView追踪触摸滑动,保证界面滑动时不受其他Mode影响。
    3. UIInitializationRunLoopMode:在刚启动App时的第一个Mode,启动完成后就不再使用。
    4. GSEventReceiveRunLoopMode:接受系统内部事件的Mode,通常用不到。
    5. NSRunLoopCommonMode:这是一个占位用的Mode,不是一种真正的Mode。

    RunLoop与Mode的关系如下:

            

    struct __CFRunLoop {
    
        CFMutableSetRef _commonModes;
        CFMutableSetRef _commonModeItems;   // Set<Source/Observer/Timer>
        CFRunLoopModeRef _currentMode;
        CFMutableSetRef _modes;
        ...
    };

      一个RunLoop可以有多个Mode,每次启动RunLoop都需要指定一种Mode,这个Mode被称为CurrentMode。如果需要切换Mode,只能退出Loop,然后再重新指定一个Mode进入。这样做的目的就是为了分隔开不同组的Source/Observer/Timer,让其互不影响。

      举个例子:我们有时候会使用定时器NSTimer:

    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0f target:self selector:@selector(doSomething) userInfo:nil repeats:YES];

    定时器启动后,每隔2秒执行一次doSomething方法。但是,如果屏幕上有个UITableView,当我们滑动UITableView时,定时器失效。这是因为上述代码等价于:

    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0f target:self selector:@selector(doSomething) userInfo:nil repeats:YES];
    [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

      可以看出,定时器默认添加到NSDefaultRunLoopMode中。而当我们滑动UITableView时,RunLoop会切换UITrackingRunLoopDefaultMode,因此定时器失效。如何让定时器在滑动UITableView时也生效呢?

      一种方法是将定时器添加到两种Mode中。另一种方法是将定时器添加到NSRunLoopCommonModes中,因为当前RunLoop的_commonModes默认包含NSDefaultRunLoopMode和UITrackingRunLoopDefaultMode。

    参考资料:  

      RunLoop

  • 相关阅读:
    第四章 利用函数实现指定的功能
    5-7 点到原点的距离(多态)
    5-2 宠物的生长(多态)
    5-7 学生cpp成绩统计
    5-6 学生CPP成绩计算
    php将远程图片下载保存到本地
    vs2010 调试快捷键
    vs2010 快捷键大全
    [C#] 使用Application.AddMessageFilter当做Form的热键
    C# 收发和处理自定义的WINDOWS消息
  • 原文地址:https://www.cnblogs.com/gattaca/p/6083098.html
Copyright © 2011-2022 走看看