Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >RunLoop详解

RunLoop详解

作者头像
老沙
发布于 2019-09-28 05:20:35
发布于 2019-09-28 05:20:35
65800
代码可运行
举报
文章被收录于专栏:老沙课堂老沙课堂
运行总次数:0
代码可运行

一、CFRunLoop部分源码

阅读源码:CFRunLoop.c

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
CFRunLoopRef CFRunLoopGetCurrent(void) {
    CHECK_FOR_FORK();
    CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
    if (rl) return rl;
    return _CFRunLoopGet0(pthread_self());
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
  ...
  //从字典获取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) {
          //如果还没有 就把新的存进去
        CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
          // 新创建的赋值给loop
        loop = newLoop;
    }
          // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
    __CFUnlock(&loopsLock);
    CFRelease(newLoop);
  }
  ...
}
从源码中可以看出

一个线程对应一个RunLoop

RunLoop保存在一个全局的字典里 线程为key RunLoop作为Value

线程刚创建的时候没有Ru nLoop对象,RunLoop会在第一次获取它的时候创建

主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop 只有调用[NSRunLoop currentRunLoop] 才创建runLoop

由于key是线程 所以在线程结束的时候RunLoop也会随之销毁

二 Runtime相关的类

1、Core Fuoundation中愿意RunLoop有5个类
CFRunLoopRef
CFRunLoopModelRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
2、CFRunLoop
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct __CFRunLoop {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;			/* locked for accessing mode list */
    __CFPort _wakeUpPort;			// used for CFRunLoopWakeUp
    Boolean _unused;
    volatile _per_run_data *_perRunData;              // reset for runs of the run loop
    pthread_t _pthread;
    uint32_t _winthread;
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;
    struct _block_item *_blocks_head;
    struct _block_item *_blocks_tail;
    CFAbsoluteTime _runTime;
    CFAbsoluteTime _sleepTime;
    CFTypeRef _counterpart;
};

重点看下面几个

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct __CFRunLoop {
   // 线程对象
    pthread_t _pthread;
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode; //当前模式CFRunLoopMode
    CFMutableSetRef _modes; // CFRunLoopModes
}

看一下CFRunLoopModeRef

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
typedef struct __CFRunLoopMode *CFRunLoopModeRef;

struct __CFRunLoopMode {
 ...
    char _padding[3];
    CFMutableSetRef _sources0;  //CFRunLoopSource
    CFMutableSetRef _sources1;  //CFRunLoopSource
    CFMutableArrayRef _observers;  //CFRunLoopObserver
    CFMutableArrayRef _timers; //CFRunLoopTimer
...
};

3、RunLoop结构

Sources0

触摸事件处理

performSelector:OnThread:

Source1

基于Port的线程间通信

系统事件的捕捉(source1 捕捉 source0处理)

Timers

NSTimer

performSelector:withObject:afterDealy:

observer

用于监听RunLoop的状态

UI刷新(BeforeWaiting)

AutoRelease pool

observer 监听类型
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0), // 进入runloop
    kCFRunLoopBeforeTimers = (1UL << 1), // 即将进入timers
    kCFRunLoopBeforeSources = (1UL << 2), // 即将进入source
    kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
    kCFRunLoopAfterWaiting = (1UL << 6), //刚从个休眠中唤醒
    kCFRunLoopExit = (1UL << 7), //即将推出Loop
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};
  • 如果model里没有任何source0/source1/observers/timers 会退出runloop
  • CFRunLoopMode
    1. CFRunLoopDefaultMode 默认
    2. UITrackingRunLoopMode 用于界面跟踪Mode用于scrollview追踪触摸滑动,保证界面滑动不受其他的mode影响
    3. CFRunLoopCommonModes

4、RunLoop运行逻辑

通过在打印调用栈 找到CFRunLoopRunSpecific

我们查看源码 看一下RunLoop的运行逻辑

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* rl, rlm are locked on entrance and exit */
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
   
    //如果有timer 处理timer   ->kCFRunLoopBeforeTimers
    if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
    //如果有Source 处理Source - >kCFRunLoopBeforeSources
    if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
   // 处理Block
__CFRunLoopDoBlocks(rl, rlm);
    //处理Source0  如果Source有block 处理Source0的block ->__CFRunLoopDoSources0
    Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
    if (sourceHandledThisLoop) {
        __CFRunLoopDoBlocks(rl, rlm);
}

    if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
        msg = (mach_msg_header_t *)msg_buffer;
        // 处理Source1中的事件 port  __CFRunLoopServiceMachPort 如果有事件  goto-> handle_msg
        if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
            goto handle_msg;
        }

    }
    //即将休眠 __CFRunLoopSetSleeping
	if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
	__CFRunLoopSetSleeping(rl);

        do {
           
            //等待唤醒
            __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
            
            if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
                // Drain the internal queue. If one of the callout blocks sets the timerFired flag, break out and service the timer.
                while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue));
                if (rlm->_timerFired) {
                    // Leave livePort as the queue port, and service timers below
                    rlm->_timerFired = false;
                    break;
                } else {
                    if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
                }
            } else {
                // Go ahead and leave the inner loop.
                break;
            }
        } while (1);

        // 被唤醒后 调用  即将唤醒
	__CFRunLoopUnsetSleeping(rl);
        //继续监听
	if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);

    
        handle_msg:;
    
       if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            // 如果有timer 处理timer; ->__CFRunLoopDoTimers
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                // Re-arm the next timer, because we apparently fired early
                __CFArmNextTimerInMode(rlm, rl);
            }
        }  else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            // 如果有timer 处理timer; ->__CFRunLoopDoTimers

            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
        // GCD唤醒
        else if (livePort == dispatchPort) {
            // ansy to MainQueue  同步到主线程
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
            
        } else {
            //处理Source1
		sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;

            
            // Restore the previous voucher
            _CFSetTSD(__CFTSDKeyMachMessageHasVoucher, previousVoucher, os_release);
        }

    //处理block
	__CFRunLoopDoBlocks(rl, rlm);
        
        //设置返回值
	if (sourceHandledThisLoop && stopAfterHandle) {
	    retVal = kCFRunLoopRunHandledSource;
        } else if (timeout_context->termTSR < mach_absolute_time()) {
            retVal = kCFRunLoopRunTimedOut;
	} else if (__CFRunLoopIsStopped(rl)) {
            __CFRunLoopUnsetStopped(rl);
	    retVal = kCFRunLoopRunStopped;
	} else if (rlm->_stopped) {
	    rlm->_stopped = false;
	    retVal = kCFRunLoopRunStopped;
	} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
	    retVal = kCFRunLoopRunFinished;
	}
    
    return retVal;
}

mach_msg()直接睡眠 内核层面

  • [ NSRunLoop currentRunLoop]run ] 是无法停止的 是一个永不销毁的线程。//可以看方法注释
  • CFRunloopRunInMode 第三个参数是 执行完source后是否退出
  • 结构体初始化最好赋值为{0} 避免内存垃圾

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 老沙说点事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
一份走心的runloop源码分析
对iOS开发者而言,runloop是一个老生常谈的话题,但凡是iOS开发者,在工作中必然直接或间接的接触过runloop。而对于面试者而言,runloop又几乎是必考点。在几年前,笔者写过一篇文章NSRunLoop,对runloop原理以及应用场景做了基本介绍。但是当时也是道听途说,简单的翻看了源码的do...while循环,并没有深入源码。所以,本文将从源码的角度剖析runloop的组成,强化自己对runloop的认识,验证我们脑海中一直以来似懂非懂的原理,真心希望这篇文章能够帮助到大家。
VV木公子
2020/05/20
9.3K0
一份走心的runloop源码分析
RunLoop 源码阅读
获取runloop的函数 // 获取主线程的runloop 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; }
用户2215591
2018/06/19
8610
RunLoop总结:RunLoop基础知识
没有实际应用场景,很难理解一些抽象空洞的东西,所以前面几篇文章先介绍了RunLoop的几个使用场景。 另外AsyncDisplayKit中也有大量使用RunLoop的示例。 关于实际的使用RunLoop 的案例和使用场景就不总结了,今天总结一点RunLoop的基础知识和概念。
Haley_Wong
2018/08/22
8590
RunLoop总结:RunLoop基础知识
Objective-C RunLoop 详解
本文转自ibireme的《深入理解RunLoop》 RunLoop 是 iOS 和 OSX 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理。之后会介绍一下在 iOS 中,苹果是如何利用 RunLoop 实现自动释放池、延迟回调、触摸事件、屏幕刷新等功能的。 目录 RunLoop 的概念 RunLoop 与线程的关系 RunLoop 对外的接口 RunLoop 的 Mode RunLoop 的内部逻辑 RunLoop 的底层实现 苹果用 R
BY
2018/05/11
1.7K0
老司机出品——源码解析之RunLoop详解
RunLoop详解 不得不说,人的惰性是真可怕啊。 从上周六就到写runLoop的建议开始,星期三告诉自己从星期四开始着手写这篇博客。然而现在戳个时间戳,现在是4.30星期日。写完发出去又不知道是什么时候啦,哈哈哈懒癌 这一期讲什么呢?这一期讲runLoop哟。一直以来,runLoop这个玄而又玄的东西似乎被当做了公司面试挑人的终极话题,原因不难想,日常开发用到runLoop的地方少之又少,没有时间的积累这方面的知识应该还是相对较于匮乏的,所以runLoop的了解侧面也能发应开发者的开发经验,当然就被当做
老司机Wicky
2018/08/22
1.3K0
老司机出品——源码解析之RunLoop详解
Runloop的再理解(Runloop下)
我们知道,Runloop可以保证线程不退出,那么,为什么Runloop具有此功效呢?那就要从Runloop的定义说起。
拉维
2019/10/28
8810
Runloop的再理解(Runloop下)
iOS多线程——你要知道的RunLoop都在这里你要知道的iOS多线程NSThread、GCD、NSOperation、RunLoop都在这里
你要知道的iOS多线程NSThread、GCD、NSOperation、RunLoop都在这里 转载请注明出处 https://cloud.tencent.com/developer/user/1605429 本系列文章主要讲解iOS中多线程的使用,包括:NSThread、GCD、NSOperation以及RunLoop的使用方法详解,本系列文章不涉及基础的线程/进程、同步/异步、阻塞/非阻塞、串行/并行,这些基础概念,有不明白的读者还请自行查阅。本系列文章将分以下几篇文章进行讲解,读者可按需查阅。 iOS
WWWWDotPNG
2018/04/10
1.6K0
iOS多线程——你要知道的RunLoop都在这里你要知道的iOS多线程NSThread、GCD、NSOperation、RunLoop都在这里
iOS多线程——RunLoop与GCD、AutoreleasePool你要知道的iOS多线程NSThread、GCD、NSOperation、RunLoop都在这里
你要知道的iOS多线程NSThread、GCD、NSOperation、RunLoop都在这里 转载请注明出处 https://cloud.tencent.com/developer/user/1605429 本系列文章主要讲解iOS中多线程的使用,包括:NSThread、GCD、NSOperation以及RunLoop的使用方法详解,本系列文章不涉及基础的线程/进程、同步/异步、阻塞/非阻塞、串行/并行,这些基础概念,有不明白的读者还请自行查阅。本系列文章将分以下几篇文章进行讲解,读者可按需查阅。 iOS
WWWWDotPNG
2018/04/10
1.9K0
深入浅出 RunLoop(三):事件循环机制
首先我们来看一下主线程的RunLoop的启动过程。 前面我们说过,我们的 iOS 程序能保持持续运行的原因就是在main()函数中调用了UIApplicationMain函数,这个函数内部会启动主线程的RunLoop。 打断点,通过 LLDB 指令bt查看函数调用栈如下:
师大小海腾
2020/04/16
9580
【IOS开发高级系列】Runloop专题
http://www.cocoachina.com/ios/20150601/11970.html
江中散人_Jun
2023/10/16
5990
【IOS开发高级系列】Runloop专题
iOS RunLoop的介绍
MelonTeam
2018/01/04
1.3K0
iOS RunLoop的介绍
RunLoop源码阅读RunLoop源码阅读
系统内核在收发事件、消息时使用的消息传递函数。可以理解为多进程之间的一种通讯调用机制。
用户8893176
2021/08/09
1.3K0
RunLoop源码阅读RunLoop源码阅读
再谈RunLoop
RunLoop 一 概述: 一句话解释RunLoop:运行任务的循环。 为什么要有RunLoop:解决交互式UI设计中的一个问题,如何快速响应用户输入,如何快速将程序运行结果输出到屏幕? 计
王大锤
2018/05/17
8490
深入浅出 RunLoop(二):数据结构
CFRunLoopObserverRef中的_activities用来保存RunLoop的活动状态。当RunLoop的状态发生改变时,通过回调_callout通知所有监听这个状态的Observer。
师大小海腾
2020/04/16
9760
iOS开发·RunLoop源码与用法完全解析(输入源,定时源,观察者,线程间通信,端口间通信,NSPort,NSMessagePort,NSMachPort,NSPortMessage)
OSX / iOS 系统中,提供了两个这样的对象:NSRunLoop 和 CFRunLoopRef。
陈满iOS
2018/09/10
2.6K0
iOS开发·RunLoop源码与用法完全解析(输入源,定时源,观察者,线程间通信,端口间通信,NSPort,NSMessagePort,NSMachPort,NSPortMessage)
RunLoop总结:RunLoop 与GCD 、Autorelease Pool之间的关系
如果在面试中问到RunLoop相关的知识,很有可能也会问到RunLoop与GCD、Autorelease Pool有没有关系,哪些地方用到了GCD、Autorelease Pool等。 So,本文就总结一下RunLoop与GCD和 Autorelease Pool 之间的关系,看看在RunLoop实现中,哪些地方间接或者直接使用、操作到了GCD 和Autorelease Pool。
Haley_Wong
2018/08/22
1.2K0
RunLoop总结:RunLoop 与GCD 、Autorelease Pool之间的关系
深入浅出 RunLoop(四):RunLoop 与线程
苹果官方文档中,RunLoop的相关介绍写在线程编程指南中,可见RunLoop和线程的关系不一般。Threading Programming Guide(苹果官方文档)
师大小海腾
2020/04/16
1.8K0
iOS源码分析(1)——RunLoop
NSRunLoop 是基于 CFRunLoopRef 的OC封装,提供了面向对象的 API,但不是线程安全的,CFRunLoopRef 是在 CoreFoundation 框架内的,它提供了纯 C 函数的 API,是线程安全的,CoreFoundation是开源的(CoreFoundation 源码地址)
羊羽shine
2019/05/29
8670
iOS 之如何利用 RunLoop 原理去监控卡顿?
1. 前言 卡顿问题,就是在主线程上无法响应用户交互的问题。如果一个 App 时不时地就给你卡一下,有 时还长时间无响应,这时你还愿意继续用它吗?所以说,卡顿问题对 App 的伤害是巨大的,也是 我们
网罗开发
2021/05/14
3K0
iOS 之如何利用 RunLoop 原理去监控卡顿?
iOS 卡顿监测方案总结
最近在写 APM 相关的东西,所以整理了一下 iOS 中卡顿监测的那些方案,不了解卡顿的原理的可以看这篇文章iOS 保持界面流畅的技巧[1],写的很好。
网罗开发
2021/11/02
2.3K0
相关推荐
一份走心的runloop源码分析
更多 >
加入讨论
的问答专区 >
后端开发擅长3个领域
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
    本文部分代码块支持一键运行,欢迎体验
    本文部分代码块支持一键运行,欢迎体验