RunLoop 和线程

RunLoop 和 线程的关系 (基本理解)

RunLoop 是线程的基础架构部分,Cocoa和CoreFundation都提供了RunLoop对象方便配置和管理线程的RunLoop。每个线程,包括程序的主线程(main thread)都有与之相应的run loop对象。
RunLoop与线程是一一对应的,一个RunLoop对应一个核心的线程,为什么说是核心的,是因为RunLoop是可以嵌套的,但是核心的只能有一个,他们的关系保存在一个全局的字典里。
RunLoop是来管理线程的,当线程的runloop被开启后,线程会在执行完任务后进入休眠状态,有了任务就会被唤醒去执行任务。
RunLoop在第一次获取时被创建,在线程结束时被销毁。
对于主线程来说,RunLoop在程序一启动就默认创建好了。
对于子线程来说,RunLoop是懒加载的,只有当我们使用的时候才会创建,所以在子线程用定时器要注意:确保子线程的RunLoop被创建,不然定时器不会回调。
但是需要注意:虽然每一个线程都可以获取RunLoop对象,但是并不是每一个线程中都有实例对象,我们可以这样理解:如果我们不获取RunLoop,这个RunLoop就不存在,我们获取时,如果不存在,就会去创建。在主线程中,这个MainRunLoop是默认创建并运行激活的。

iOS的应用程序启动后会有一个如下的main()函数

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

这个方法会为main thread设置一个NSRunLoop对象,为什么是我们的应用可以在无人操作的时候休息,需要让它干活的时候又能立马响应。
对其它线程来说,NSRunLoop 默认是没有启动的,如果你需要更多的线程交互则可以手动配置和启动,如果线程只是去执行一个长时间的已确定的任务则不需要。
在任何一个Cocoa程序的线程中,都可以通过以下代码来获取到当前线程的NSRunLoop。

NSRunLoop *runloop = [NSRunLoop currentRunLoop];

RunLoop执行完毕之后,就会进入休眠 , 只有在某个情况下触发了,才会再次调用;

RunLoop 和 线程的关系 (代码分析)

首先,iOS 开发中能遇到两个线程对象: pthread_t 和 NSThread。过去苹果有份文档标明了 NSThread 只是 pthread_t 的封装,但那份文档已经失效了,现在它们也有可能都是直接包装自最底层的 mach thread。苹果并没有提供这两个对象相互转换的接口,但不管怎么样,可以肯定的是 pthread_t 和 NSThread 是一一对应的。比如,你可以通过 pthread_main_thread_np() 或 [NSThread mainThread] 来获取主线程;也可以通过 pthread_self() 或 [NSThread currentThread] 来获取当前线程。CFRunLoop 是基于 pthread 来管理的。

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

/// 全局的Dictionary,key 是 pthread_t, value 是 CFRunLoopRef
static CFMutableDictionaryRef loopsDic;
/// 访问 loopsDic 时的锁
static CFSpinLock_t loopsLock;
 
/// 获取一个 pthread 对应的 RunLoop。
CFRunLoopRef _CFRunLoopGet(pthread_t thread) {
    OSSpinLockLock(&loopsLock);
    
    if (!loopsDic) {
        // 第一次进入时,初始化全局Dic,并先为主线程创建一个 RunLoop。
        loopsDic = CFDictionaryCreateMutable();
        CFRunLoopRef mainLoop = _CFRunLoopCreate();
        CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop);
    }
    
    /// 直接从 Dictionary 里获取。
    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(主线程除外)。

你可能感兴趣的:(RunLoop 和线程)