iOS多线程——你要知道的RunLoop都在这里

本文详细讲解了iOS中Run Loop的基本概念、源码解析及其执行逻辑,包括它在多线程编程中的作用,以及与NSThread、GCD、NSOperation的关系。文中还介绍了RunLoop的Source、Timer、Observer和Mode,帮助开发者深入了解RunLoop的工作原理。
摘要由CSDN通过智能技术生成

你要知道的iOS多线程NSThread、GCD、NSOperation、RunLoop都在这里

转载请注明出处 http://blog.csdn.net/u014205968/article/details/78323193

本系列文章主要讲解iOS中多线程的使用,包括:NSThread、GCD、NSOperation以及RunLoop的使用方法详解,本系列文章不涉及基础的线程/进程、同步/异步、阻塞/非阻塞、串行/并行,这些基础概念,有不明白的读者还请自行查阅。本系列文章将分以下几篇文章进行讲解,读者可按需查阅。

RunLoop 基本概念

前面几篇文章详细讲解了创建多线程的方法和多线程编程的相关知识,当我们使用NSThread进行多线程编程时,只要任务结束,线程也就退出了,每次执行一个任务都需要创建一个线程非常浪费资源,所以需要一种能够使线程常驻内存不退出d,当有任务来临时能随时执行的方法,这就是RunLoop的作用。类似于javascriptEvent Loop模型,大致类似于如下代码:

int retVal = Running;
do {
     // 执行各种任务,处理各种事件
     // ......
} while (retVal != Stop && retVal != Timeout);

上述循环只有在特定条件才才会退出,否则就会一直在循环中处理各种任务或事件,诸如触摸屏幕事件、手势事件、定时器事件、用户提交的任务、各种方法的执行等。

RunLoop与线程关联的,是一种事件处理环,用来安排和协调到来的事件,目的就是让其关联的线程在有事件到达时时刻保持运行状态,而当没有事件需要处理时进入睡眠状态从而节约资源,每一个线程都可以有一个RunLoop对象与之对应,并且是在第一次获取它是系统自动创建的,比如主线程关联的RunLoop,我们都知道程序的入口函数是main函数,下面是创建工程后Xcode自动生成的main.m文件的main函数代码:

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

该方法执行体被autoreleasepool包围,所以程序可以使用ARC来管理内存,后面会讲解RunLoopautoreleasepool的关系,main函数直接返回了UIApplicationMain函数,该函数内部就会第一次获取RunLoop对象,所以系统就会创建这样一个RunLoop对象,因此在没有满足特定条件的时候该主线程不会退出,应用就可以持续运行而不会退出。

在官方文档中使用下图描述RunLoop模型:

官方RunLoop模型图

从上图可以看出一个线程会关联一个RunLoop对象,RunLoop对象会一直循环,直到超时或收到退出指令。在无限循环的过程中会一直处理到来的事件,右侧将事件分为了两类,一类是Input sources这部分包括基于端口的source1事件,开发者提交的各种source0事件,调用performSelector:onThread:方法事件,还有一类Timer sources这个就是常用的定时器事件,这些事件在程序运行期间会不断产生之后会由RunLoop对象检测并负责处理相关事件。

RunLoop 源码解析

RunLoop有两个对象,NSRunLoopCFRunLoopRef,区别在于由Core Foundation框架提供的CFRunLoopRef是纯C语言编写的,提供的也是C语言接口,这些接口都是线程安全的,由Foundation框架提供的NSRunLoop是面向对象的,它是基于CFRunLoopRef的封装,提供的都是面向对象的接口,但这些接口不是线程安全的,Core Foudation框架是开源的,可以在这个地址下载:Core Foundation开源代码,本文接下来的内容主要是针对该开源代码进行讲解。

首先,看一下在代码中如何获取RunLoop对象,在Foundation框架中的NSRunLoop类提供了如下两个类属性:

//获取当前线程关联的RunLoop对象
@property (class, readonly, strong) NSRunLoop *currentRunLoop;
//获取主线程关联的RunLoop对象
@property (class, readonly, strong) NSRunLoop *mainRunLoop

对应的Core Foundation框架中提供了如下两个函数来获取RunLoop对象:

//获得当前线程关联的RunLoop对象
CFRunLoopGetCurrent(); 
// 获得主线程关联的RunLoop对象
CFRunLoopGetMain();

前面一直讲每一个线程都会关联一个RunLoop对象,并且不能通过手动创建该对象,只能在第一次获取时系统自动创建,看一下Core Foundation框架是如何实现的:

//CFRunLoopGetMain函数用于获取主线程关联的RunLoop对象
CFRunLoopRef CFRunLoopGetMain(void) {
    CHECK_FOR_FORK();
    //静态变量保存主线程关联的RunLoop对象
    static CFRunLoopRef __main = NULL; // no retain needed
    //如果主线程关联的RunLoop对象为NULL就调用_CFRunLoopGet0函数获取一个
    if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
    return __main
}

//获取当前线程关联的RunLoop对象
CFRunLoopRef CFRunLoopGetCurrent(void) {
    CHECK_FOR_FORK();
    //这一段没找到对应的函数...猜测是和上面的函数用意一样
    CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
    if (rl) return rl;
    //如果上面没找到就调用_CFRunLoopGet0函数去获取一个
    return _CFRunLoopGet0(pthread_self());
}


//全局的可变字典数据结构,key为thread_t即线程,value为RunLoop对象
static CFMutableDictionaryRef __CFRunLoops = NULL;
//全局的一个锁
static CFLock_t loopsLock = CFLockInit;

//_CFRunLoopGet0接收一个pthread_t对象,即线程对象,返回一个与之关联的RunLoop对象
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
    //判断是否为主线程
    if (pthread_equal(t, kNilPthreadT)) {
    //pthread_main_thread_np()函数用来获取主线程
    t = pthread_main_thread_np();
    }
    //加锁,防止产生竞争创建多个RunLoop对象
    __CFLock(&loopsLock);
    //如果全局的保存线程和runloop对象的字典为空
    if (!__CFRunLoops) {
        __CFUnlock(&loopsLock);
    //创建一个字典
    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
    /*
    根据主线程创建RunLoop对象
    所以,当第一次获取RunLoop对象时就会自动创建主线程关联的RunLoop对象
    */
    CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
    //设置全局的字典,key为主线程,value为主线程关联的RunLoop对象
    CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
    if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
        CFRelease(dict);
    }
    CFRelease(mainLoop);
        __CFLock(&loopsLock);
    }
    //通过线程在字典中获取RunLoop对象
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    __CFUnlock(&loopsLock);
    //如果没有获取到
    if (!loop) {
    //没有获取到就根据线程创建一个RunLoop对象
    CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        __CFLock(&loopsLock);
    //再次获取
    loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    if (!loop) {
        //字典中仍然没有线程关联的RunLoop对象就将刚才新创建加入到字典照中
        CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
        loop = newLoop;
    }
        // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
        __CFUnlock(&loopsLock);
    CFRelease(newLoop);
    }
    if (pthread_equal(t, pthread_self())) {
        _CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值