iOS—RunLoop详解

什么是RunLoop

一般来说,一个线程一次只能执行一个任务,执行完成后线程就会退出。就比如之前学OC时使用的命令行程序,执行完程序就结束了。
而runloop,运行循环,可使线程能随时处理时间但并不退出,这也就是手机app在运行时不会退出的原因。当没有事件时,runloop会进入休眠状态,有事件发生时,runloop再进行相应的处理事件。runloop可以让线程在需要做事的时候忙起来,不需要的时候让线程休眠。

runloop基本作用

  • 保持程序的持续运行
  • 处理app中各种事件
  • 节省CPU资源,提高程序性能:该做事时做事,该休眠时休眠。休眠时不占用CPU

runloop实际上是一个对象,这个对象管理了其需要处理的事件和消息,并提供了一个入口函数来执行下面图片中的逻辑。线程执行了这个函数后,就会处于这个函数内部的循环中,直到循环结束,函数返回。
在这里插入图片描述

获取runloop

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

  • Foundation:NSRunLoop
  • Core Foundation:CFRunLoopRef

NSRunLoop和CFRunLoopRef都代表着RunLoop对象,NSRunLoop是基于CFRunLoopRef的一层oc封装
在这里插入图片描述

苹果不允许直接创建runloop对象,可通过下面的方法获取runloop对象。

    //获取主线程的runloop
    NSRunLoop *runloop1 = [NSRunLoop mainRunLoop];
    CFRunLoopRef runloop2 = CFRunLoopGetMain();
    
    //获取当前线程的runloop对象
    NSRunLoop *runloop3 = [NSRunLoop currentRunLoop];
    CFRunLoopRef runloop4 = CFRunLoopGetCurrent();

下面的打印显示,虽然两种runloop打印的地址不一样,但是33行打印NSRunLoop时显示的地址却是CFRunLoop,也就说明了两种RunLoop之间的关系就如同上面图片一样,NSRunLoop是对CFRunLoop的一层oc封装。
在这里插入图片描述

CFRunLoopGetMain、CFRunLoopGetCurrent的实现如下

CFRunLoopRef CFRunLoopGetMain(void) {
   
    CHECK_FOR_FORK();
    static CFRunLoopRef __main = NULL; // no retain needed
    if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
    return __main;
}

CFRunLoopRef CFRunLoopGetCurrent(void) {
   
    CHECK_FOR_FORK();
    CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
    if (rl) return rl;
    return _CFRunLoopGet0(pthread_self());
}

这两个方法其实都是调用了_CFRunLoopGet0,_CFRunLoopGet0的实现如下


//全局的Dictionary,key为线程,value是runloop
static CFMutableDictionaryRef __CFRunLoops = NULL;
//访问loopsDic 时的锁
//锁的目的是:考虑线程安全问题,防止多条线程同时访问 __CFRunLoop对应的内存空间
static CFLock_t loopsLock = CFLockInit;

// should only be called by Foundation
// t==0 is a synonym for "main thread" that always works

//获取一个线程对应的runloop
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
   
    //线程t为空时,获取主线程赋给t
    if (pthread_equal(t, kNilPthreadT)) {
   
	t = pthread_main_thread_np();
    }
    __CFLock(&loopsLock);
    
    //如果字典为空时,也就是第一次进入
    if (!__CFRunLoops) {
   
        __CFUnlock(&loopsLock);
        //创建一个临时字典dict
        CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
        //根据主线程创建主线程对应的runloop
        CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
        //保存主线程,将主线程(key)和对应的runloop(value)保存到字典中
        CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
        //把临时字典dict赋值给全局字典中
        if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
   
        	//赋值成功,释放dict
            CFRelease(dict);
        }
        //存储mainLoop到字典后,撤销一次mainloop的引用,因为mainLoop存储到字典后会自动retain
        CFRelease(mainLoop);
        __CFLock(&loopsLock);
    }
    
    //从字典中根据线程(key)获取对应的runloop
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    __CFUnlock(&loopsLock);
    //如果没有取到,说明字典中没有对应的loop,获取不到就创建并存储
    //在子线程中第一次获取RunLoop是获取不到的,然后才去创建,这个if语句一般在子线程中第一次获取当前runloop才会进去执行
    if (!loop) {
   
        //创建一个新的runloop
        CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        __CFLock(&loopsLock);
    	//再一次从全局的字典 __CFRunLoops中获取对应于当前线程的RunLoop
        loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
        //如果还是获取不到RunLoop,就存储当前线程和刚创建的RunLoop到全局字典 __CFRunLoops
        if (!loop) {
   
            //将新的runloop(value)和线程(key)存入全局字典中
            CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
            //将新的runloop赋给loop
            loop = newLoop;
        }
        // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
        __CFUnlock(&loopsLock);
        //release局部的newloop
        CFRelease(newLoop);
    }
    //判断t是否为当前线程,如果是就注册一个回调,当线程销毁的时候,也销毁其对应的RunLoop
    if (pthread_equal(t, pthread_self())) {
   
        _CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
        
  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值