IOS开发入门之RunLoop总结:RunLoop基础知识
凌雪 2018-11-09 来源 :网络 阅读 739 评论 0

摘要:本文将带你了解IOS开发入门RunLoop总结:RunLoop基础知识,希望本文对大家学IOS有所帮助。

本文将带你了解IOS开发入门RunLoop总结:RunLoop基础知识,希望本文对大家学IOS有所帮助。


       

RunLoop总结:RunLoop基础知识

没有实际应用场景,很难理解一些抽象空洞的东西,所以前面几篇文章先介绍了RunLoop的几个使用场景。
另外AsyncDisplayKit中也有大量使用RunLoop的示例。
关于实际的使用RunLoop   的案例和使用场景就不总结了,今天总结一点RunLoop的基础知识和概念。

什么是RunLoop?

顾名思义,它就是一个运行循环。一个RunLoop   就是一个用于处理既定工作和接收到的外来事件的事件处理循环。RunLoop的存在目的就是当线程中有任务时,保证线程忙着干活;当线程中没有任务时,让线程睡眠,以节省资料(想想看,你是在房间里一直转圈抗饿还是躺在床上睡觉更抗饿?)。
理解了   EventLoop 就   能很好的理解RunLoop了。
简单的用伪代码来表示就是这样的:


   

function loop() {

    initialize();

    while (message != quit) {

        var message = get_next_message();

        process_message(message);

    }

}

   

关于RunLoop,苹果的Cocoa   和 CoreFoundation 框架都分别提供了NSRunLoop 和   CFRunLoopRef供开发者调用和执行操作。 CFRunLoopRef 只是一个结构体,而   NSRunLoop是一个NSObject 对象,必然是苹果将   CFRunLoopRef进行了封装。
需要注意的是NSRunLoop并不是线程安全的,而   CFRunLoopRef   是线程安全的。
官方文档原文是:


   

<code><code><code>Thread   safety varies depending on which API you are using to manipulate your run   loop.

The functions in Core   Foundation are generally thread-safe and can be called from any thread.  

If you are performing   operations that alter the configuration of the run loop, however,  

it is still good   practice to do so   from the thread that owns the run loop whenever   possible.

 

The Cocoa NSRunLoop   class   is not as inherently thread safe as   its Core Foundation counterpart.

If you are using the NSRunLoop class to modify your run loop, you should   do   so only from the same thread that   owns that run loop.

Adding an input   source or timer to a run loop belonging to a different thread could cause   your code to crash or behave in an unexpected   way.</code></code></code>

   

接下来,看一下CFRunLoopRef里都保存了哪些数据?
可以从CF框架源码 的 CFRunLoop.h和CFRunLoop.c,看看   苹果对 CFRunLoopRef 的定义。
CFRunLoopRef是   结构体__CFRunLoop *的重命名,由 typedef struct __CFRunLoop *   CFRunLoopRef; 可知;
__CFRunLoop   的定义:


   

<code><code><code><code><code><code>struct   __CFRunLoop {

    CFRuntimeBase   _base;

    pthread_mutex_t   _lock;            /* locked for   accessing mode list,每次读取mode list 要加锁 */

    __CFPort _wakeUpPort;             // used for   CFRunLoopWakeUp

    Boolean   _unused;

    volatile _per_run_data   *_perRunData;                // reset for runs of   the run loop

    pthread_t   _pthread;                   //与该runLoop   关联的线程

    uint32_t   _winthread;

    CFMutableSetRef   _commonModes;         // set 中保存的就是   NSRunLoopCommonModes表示的mode,我们也可以将自定义的mode 添加到这个set   里。

    CFMutableSetRef   _commonModeItems;   //添加到NSRunLoopCommonModes中的source/timer等item   都会被添加到这个set里,这在应用场景一中有打印出来。

    CFRunLoopModeRef   _currentMode;        //RunLoop   当前执行的是哪个mode

    CFMutableSetRef   _modes;               // 该runLoop   中所有的mode

    struct _block_item   *_blocks_head;

    struct _block_item   *_blocks_tail;

    CFAbsoluteTime   _runTime;

    CFAbsoluteTime   _sleepTime;

    CFTypeRef   _counterpart;

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

   

再来看一下RunLoopMode   的结构,之前说过RunLoopMode 中存放的是两种source/timer/observer,而   CFRunLoopModeRef是struct __CFRunLoopMode   *重命名的(typedef struct __CFRunLoopMode *CFRunLoopModeRef;),   看下定义就明白了:



   

<code><code><code><code><code><code><code><code><code>struct   __CFRunLoopMode {

    CFRuntimeBase   _base;

    pthread_mutex_t _lock;    /* must have the run   loop locked before locking this */

    CFStringRef   _name;                //mode   的name

    Boolean   _stopped;

    char _padding[3];

    CFMutableSetRef   _sources0;      // 保存所有source0   的set

    CFMutableSetRef   _sources1;        // 保存所有source1 的set  

    CFMutableArrayRef   _observers;   // 保存所有observer 的数组

    CFMutableArrayRef _timers;        // 保存所有timer   的数组

    CFMutableDictionaryRef   _portToV1SourceMap;

    __CFPortSet   _portSet;

    CFIndex   _observerMask;

#if USE_DISPATCH_SOURCE_FOR_TIMERS

    dispatch_source_t   _timerSource;

    dispatch_queue_t   _queue;

    Boolean _timerFired; // set to true by the source when a timer   has fired

    Boolean   _dispatchTimerArmed;

#endif

#if USE_MK_TIMER_TOO

    mach_port_t   _timerPort;

    Boolean   _mkTimerArmed;

#endif

#if DEPLOYMENT_TARGET_WINDOWS

    DWORD   _msgQMask;

    void (*_msgPump)(void);

#endif

    uint64_t _timerSoftDeadline;   /* TSR   */

    uint64_t _timerHardDeadline;   /* TSR   */

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

   

看完上面   __CFRunLoopMode 和 __CFRunLoop的定义,关于 RunLoop   中保存的是RunLoopMode,而RunLoopMode中保存的才是实际的任务这点没有疑问了。

如何创建一个RunLoop?

包括MainRunLoop在内,每一个RunLoop都与一个线程关联着。确切的说,是先有线程,再有RunLoop。
关于线程与RunLoop的关系,在RunLoop官方文档的第一节讲的很清楚。
我们不用,也最好不要显示的创建RunLoop,苹果提供了两个API,便于我们来获取RunLoop。
CFRunLoopGetMain()   和   CFRunLoopGetCurrent(),分别用于获取MainRunLoop和当前线程的RunLoop(在主线程中调用CFRunLoopGetCurrent()与CFRunLoopGetMain()获取的其实都是MainRunLoop)。

先来看一下,这两个函数的源码实现:



   

<code><code><code><code><code><code><code><code><code><code><code><code><code><code>CFRunLoopRef   CFRunLoopGetMain(void)   {

    CHECK_FOR_FORK();

    static CFRunLoopRef __main = NULL;   // no retain   needed

    //通过_CFRunLoopGet0   这个关键函数,取出MainRunLoop。

    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;

    //通过_CFRunLoopGet0 这个关键函数,取出当前RunLoop。

    return _CFRunLoopGet0(pthread_self());

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

   

从以上源码,可以看出RunLoop   是通过 _CFRunLoopGet0函数来获取的,并且以线程作为参数。
这个函数的作用与 通过 key 从   NSDictionary 获取Value   极为相似。

接下来,看一下   _CFRunLoopGet0   的实现(太长不想看,可以看下面的伪代码):



   

<code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code>static CFMutableDictionaryRef __CFRunLoops =   NULL;

static   CFLock_t loopsLock =   CFLockInit;

 

// should only be   called by Foundation

// t==0 is a   synonym for "main thread" that always   works

CF_EXPORT CFRunLoopRef   _CFRunLoopGet0(pthread_t t) {

    if (pthread_equal(t, kNilPthreadT))   {

    t =   pthread_main_thread_np();

    }

    __CFLock(&loopsLock);

    if (!__CFRunLoops) {

        __CFUnlock(&loopsLock);

    CFMutableDictionaryRef dict =   CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL,   &kCFTypeDictionaryValueCallBacks);

    CFRunLoopRef mainLoop =   __CFRunLoopCreate(pthread_main_thread_np());

    CFDictionarySetValue(dict,   pthreadPointer(pthread_main_thread_np()), mainLoop);

    if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict,   (void   * volatile *)&__CFRunLoops))   {

        CFRelease(dict);

    }

    CFRelease(mainLoop);

        __CFLock(&loopsLock);

    }

    CFRunLoopRef loop =   (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops,   pthreadPointer(t));

    __CFUnlock(&loopsLock);

    if (!loop) {

    CFRunLoopRef newLoop =   __CFRunLoopCreate(t);

        __CFLock(&loopsLock);

    loop =   (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops,   pthreadPointer(t));

    if (!loop) {

        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()))   {

&n    

   

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

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

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

我知道了

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

请输入正确的手机号码

请输入正确的验证码

获取验证码

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

提交

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

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

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

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程