IOS开发入门之iOS多线程之RunLoop概念及使用技巧
凌雪 2018-10-23 来源 :网络 阅读 1193 评论 0

摘要:本文将带你了解IOS开发入门iOS多线程之RunLoop概念及使用技巧,希望本文对大家学IOS有所帮助。

本文将带你了解IOS开发入门iOS多线程之RunLoop概念及使用技巧,希望本文对大家学IOS有所帮助。


         

iOS多线程之RunLoop概念及使用技巧。

RunLoop   基本概念

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


   

<code><code><code>int retVal = Running;

do {

     //   执行各种任务,处理各种事件

     // ......

} while   (retVal != Stop &&   retVal !=   Timeout);</code></code></code>

   

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

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


   

<code><code><code><code><code><code>int main(int argc, char *   argv[]) {

    @autoreleasepool {

        return UIApplicationMain(argc, argv, nil,   NSStringFromClass([AppDelegate class]));

    }

}</code></code></code></code></code></code>

   

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

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

RunLoop   源码解析

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

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


   

<code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code>//获取当前线程关联的RunLoop对象

@property (class,   readonly, strong) NSRunLoop *currentRunLoop;

//获取主线程关联的RunLoop对象

@property (class,   readonly, strong) NSRunLoop   *mainRunLoop</code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code>

   

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


   

<code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code>//获得当前线程关联的RunLoop对象

CFRunLoopGetCurrent();

// 获得主线程关联的RunLoop对象

CFRunLoopGetMain();</code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code>

   

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



<code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code>//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,   NULL);

        if (0 ==   _CFGetTSD(__CFTSDKeyRunLoopCntr)) {

            //设置销毁时的回调

            _CFSetTSD(__CFTSDKeyRunLoopCntr,   (void   *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);

        }

    }

    //返回线程关联的RunLoop对象

    return loop;

}

 

/*

真正的用于创建RunLoop对象的静态函数,形参为线程对象

该函数主要用于分配存储空间,并进行RunLoop对象相关初始化操作

*/

static CFRunLoopRef __CFRunLoopCreate(pthread_t t)   {

    CFRunLoopRef loop =   NULL;

    CFRunLoopModeRef   rlm;

    uint32_t size = sizeof(struct __CFRunLoop) -   sizeof(CFRuntimeBase);

    loop =   (CFRunLoopRef)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault,   CFRunLoopGetTypeID(), size, NULL);

    if (NULL == loop) {

    return NULL;

    }

    (void)__CFRunLoopPushPerRunData(loop);

    __CFRunLoopLockInit(&loop->_lock);    

   

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之IOS频道!

本文由 @凌雪 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式AI+学习就业服务平台 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved