RunLoop底层原理

一、RunLoop介绍

1.1概念:(关键字:运循 内维事循对事/消 管 对象)

运行循环,程序运行过程中循环做一些事情。通过内部维护的事件循环对事件/消息进行管理的一个对象。

1.1.2RunLoop休眠实现原理(关键字: 等消息 无线休 有唤醒 用户态->内核态 )

等待消息,没有消息就让线程休眠,用户态到内核态切换;

有消息就唤醒线程,处理事件,内核态到用户态切换。

1.1.3main函数作为启动入口,顺着执行体代码以此执行,最后main函数退出,程序退出。main为什么能保持不退出?正常执行体退出main就退出,而我们的main一直是活跃状态 main函数当中UIApplicationMain( )函数,内部启动RunLoop运行循环,接收消息,如滑动列表,网络请求的返回,然后处理。处理完成后主线程处于休眠状态,继续等待,不是死循环,不是简单for或while,而是有用户态到内核态的状态的切换,避免资源占用。若此时点击屏幕产生machPort,最终转成source1,可以把主线程唤醒,运行,然后处理事件。当程序杀死时,就会发送通知即将退出RunLoop,然后RunLoop退出,线程销毁掉。

1.1.4点击应用图标

1.2应用:(关键字:定 GAMQ 事手界 网 A)

定时器NSTimer、PerformSelector

GCD Async Main Queue

事件响应、手势识别、界面刷新

网络请求

AutoreleasePool

实际项目中应用:(关键字:线活 解NSTimer 监卡 性优)

控制线程生命周期(保证线程活跃)

解决滑动时NSTimer生效问题

监控应用卡顿

性能优化

1.3作用:(关键字:持运 处事 节CPU提性能)

保持程序持续运行

处理各种事件如触摸、定时器等

节省CPU、提高性能,适时做事或休息

1.4两套访问和使用RunLoop的API

Fundation: NSRunLoop是对CFRunLoopRef的OC封装,提供了面向对象的API

CoreFoundation: CFRunLoopRef开源https://opensource.apple.com/tarballs/CF/

1.5获取方法 获当线current 获主线main

[NSRunLoop currentRunLoop]; CFRunLoopGetCurrent( );

[NSRunLoop mainRunLoop]; CFRunLoopGetMain( );

二、数据结构(关键字:CFRunLoopRef, Mode, Source, Timer, Observer)

CFRunLoopRef

typedef struct __CFRunLoop *CFRunLoopRef;

struct __CFRunLoop{

pthread_t pthread;//一一对应(RunLoop与多线程的关系)

CFRunLoopModeRef _currentMode;

CFMutableSetRef _modes;//CFRunLoopMode集合

CFMutableSetRef _commonModes;//NSString类型的集合

CFMutableSetRef _commonModeItems;//包含多个Source/Timer/Observer

}

CFRunLoopModeRef(关键字:运模 一对多Mode,一Mode对多Source0/Source1/Timer/Observer Loop启只选一currentMode 切换退重选 无Source0/Source1/Timer/Observer,Loop即退)

typedef__CFRunLoopMode *CFRunLoopModeRef;

struct __CFRunLoopMode{

CFString _name;

CFMutableSetRef _sources0;

CFMutableSetRef _sources1;

CFMutableArrayRef _observers;

CFMutableArrayRef _timers;

}

CFRunLoopSource

source0需要手动唤醒线程 source1具备唤醒线程的能力

CFRunLoopTimer基于事件的定时器

与NSTimer是toll_free bridged可免费桥转换

CFRunLoopObserver观测时间点 进入 即将处理Timer Source 即将休眠 刚从休眠中唤醒 退出

KCFRunLoopEntry/BeforeTimes/BeforeSources/BeforeWaiting/AfterWaiting/Exit

RunLoop的运行模式

各数据结构间关系:一个RunLoop有若干Mode,一个Mode有若干Source0/Source1/Timer/Observer

RunLoop启动只选择一个Mode作为currentMode

若要切换Mode只能退出当前Mode,再重选Mode进入

若Mode没有任何Source0/Source1/Timer/Observer,RunLoop会立马退出 当RunLoop运行在某个Mode上,如Mode1,此时另一个Mode如2中某个observer或timer回调了,此时无法接收到Mode2中回调过来的Observer以及timer事件,这就是RunLoop有多个Mode的原因,起到了屏蔽的效果,当运行在Mode1中只能接收和处理Mode1中的Sources、Observers、Timers

NSRunLoopCommonModes不是实际存在的Mode 是同步Source/Timer/Observer到多个Mode中的一种技术方案

常见2种Mode(Default默主运或 UITracking踪滑不受其他影响)

KCFRunLoopDefaultMode(NSDefaultRunLoopMode)默认Mode,主线程在该Mode下运行

UITrackingRunLoopMode跟踪触摸滑动,保证界面滑动是不受其他Mode影响

CFRunLoopObserverRef(关键字:Entry BeforeTimers BeforeSources BeforeWaiting AferWaiting Exit AllActivities)

typedef CF_OPTIONS(CFOptionFlags,CFRunLoopActivity){

kCFRunLoopEntry = (1UL <<0),

1,

2,

5,

6,

7,

0X0FFFFFFFU

}

三、RunLoop运行逻辑/事件循环机制

(关键字:1/2/3/7/8/11通知Observer 1.进Loop 2.将处理Timer 3.Sources 4.处理Blocks 5.Source0可能再Blocks 6.若存Source1结束休眠 7.无则休 8.结束休眠处理Timer、GAMQ Source1 9.处理Blocks 10.据之前执行结果决定如何操作回2或退出Loop 11.退出RunLoop)

无论调用NSRunLoop还是CFRunLoopRef的run最终都会调用void RunLoopRun( )

1.通知Observer: 进入Loop 即将处理Timer 即将处理Sources

2.处理blocks Source0可能还会处理blocks 若有Sounces1结束休眠

3.通知Observer:若无Sounces1则休眠 有Source1/Timer事件回调/外部手动唤醒等被唤醒结束休眠,处理Timer GCD Async Main Queue

4.处理Blocks 根据之前执行结果决定如何操作:回2或退出RunLoop

5.通知Observer:退出RunLoop

四、RunLoop与NSTimer

滑动tableView时我们的定时器不生效了?怎么解决呢?默认运行在KCFRunLoopDefaultMode,当滑动时发生Mode切换,切换到UITrackingRunLoopMode 可通过CFRunLoopAddTimer函数把timer添加到commonMode(不是实际的mode只是把一些mode打上common标记,然后可以把事件源比如timer同步到多个mode)中

CFRunLoopAddTimer(runLoop, timer,commonMode)函数具体实现:若当前模式名称是commonModes会提取runloop的commonModes,是一堆字符串元素,然后判断其对应的commonModeItems是否为空,若为空,会重新创建集合。然后timer添加到commonModeItems集合,然后把runloop和timer封装到context,之后对集合中每一个元素都调用_CFRunLoopAddItemToCommonModes函数,该函数参数集合中对象元素和context。

CFRunLoopAddItemToCommonModes

Timer如果想同时加入两个Mode应该怎么处理?

五、RunLoop与多线程(关键字:一对一 主自建 子第一次获取建 全局字典 结时销)

5.1每个线程都有与之一一对应的RunLoop

主线程的RunLoop自动创建,子线程默认不开启

子线程在第一次被获取时,创建RunLoop

RunLoop存在于全局字典中,线程作为key,RunLoop作为value

线程结束时RunLoop销毁

5.2怎么实现常驻线程?

为当前线程开启一个RunLoop,

向该RunLoop中添加一个Port/Source等维持RunLoop的事件循环,

启动该RunLoop 运行模式和资源添加的模式必须是同一个,否则可能由于外部使用while循环导致死循环

5.3怎样保证子线程数据回来更新UI的时候不打断用户的滑动操作?

用户滑动过程中,当前RunLoop运行在UITrackingRunLoopMode,而一般对网络请求放在子线程进行,子返回给主的数据主线程用来更新UI,

此时可通过把子线程抛回给主线程进行UI更新的逻辑包装起来,提交到主线程的default模式下,当前用户滑动过程中,处于TrackingRunLoopMode下,分派到default模式下的任务不会执行。

当停止滑动时,当前线程的mode切换到default模式下,会处理子线程上抛给子线程的任务。

<think>嗯,用户想了解RuntimeRunloop底层原理,这两个都是iOS开发中的核心概念。首先,我需要回忆一下自己之前学过的内容,确保理解正确。Runtime主要是Objective-C的动态运行时环境,而Runloop则与事件处理循环相关。 先从Runtime开始。记得Runtime库是用C汇编写的,它处理对象的创建、消息转发、方法交换等等。可能需要解释一下消息发送机制,比如objc_msgSend的作用,还有方法解析消息转发的流程。另外,类与对象的结构,如isa指针、方法列表、属性列表这些也是关键点。动态特性如方法交换(method swizzling)关联对象(associated objects)也应该提到,这些都是Runtime的实际应用场景。 然后是RunloopRunloop的基本概念是事件处理循环,保持程序持续运行并处理各种事件。需要区分主线程子线程的Runloop,主线程的默认开启,子线程的需要手动启动。Runloop的模式,比如DefaultModeTrackingMode,以及不同模式下如何处理事件。还要讲Runloop的结构,Sources、Timers、Observers这些组成部分,以及它们如何协同工作。最后,Runloop在应用中的实际作用,比如界面刷新、定时器、事件响应等,都是必须涵盖的点。 用户可能对底层原理感兴趣,但作为开发者,他们可能更关心这些机制在实际开发中的应用,比如性能优化、解决卡顿问题,或者如何利用Runtime进行动态方法添加。需要确保解释清晰,结构合理,避免过于晦涩。同时,注意使用中文,按照系统指令正确排版数学公式,不过这里可能用不上太多数学内容,但如果有涉及的地方要记得用$符号包裹。 可能需要注意用户的知识水平,如果用户是初学者,可能需要更简洁的解释,避免过多技术细节。但问题中提到了“底层原理”,所以需要深入一些。但也要保持条理,分点说明。同时,检查是否有遗漏的重要概念,比如自动释放池与Runloop的关系,或者Runloop如何与GCD交互。 最后,确保回答结构清晰,分RuntimeRunloop两部分,每部分有概述、核心机制、实际应用等小节。使用例子帮助理解,比如消息转发的步骤,或者Runloop处理事件的流程。避免错误,比如混淆Runloop线程的关系,或者错误描述方法解析的顺序。需要确认信息的准确性,必要时参考官方文档或可靠的资料。</think>好的,我将以iOS/macOS开发为背景,分步解释RuntimeRunloop底层原理: ### 一、Runtime(运行时系统) #### 1. 核心概念 - Objective-C的动态特性基础 - C语言汇编实现的库(源码可见于[objc4](https://opensource.apple.com/source/objc4/)) - 核心职责:实现面向对象特性消息传递机制 #### 2. 核心机制 $$Class结构体 = isa指针 + superclass指针 + method\_list\_t + cache\_t + ...$$ **消息传递流程**: 1. 快速查找:通过`objc_msgSend`在方法缓存`cache_t`中查找 2. 慢速查找:遍历类的方法列表`method_list_t` 3. 动态解析:未找到时触发`+resolveInstanceMethod:` 4. 消息转发:经过`forwardingTargetForSelector:``methodSignatureForSelector:`两阶段 **关键数据结构**: - `isa指针`:实现对象与类的关联(arm64架构包含non-pointer isa优化) - `method_t`:包含SEL(方法选择器)、IMP(函数指针)、TypeEncoding(类型编码) #### 3. 实际应用 - 方法交换(Method Swizzling) - 动态添加方法(`class_addMethod`) - 关联对象(Associated Objects) - KVO实现(通过派生子类重写setter方法) --- ### 二、Runloop(运行循环) #### 1. 核心概念 - 事件处理循环:`do-while`循环保持线程活跃 - 与线程一一对应(主线程Runloop自动启动,子线程需手动启动) - 核心作用:管理事件/消息、进行线程调度 #### 2. 核心结构 ```plaintext CFRunLoop { currentMode = "UIInitializationRunLoopMode", modes = [ CFRunLoopMode { name = "kCFRunLoopDefaultMode", sources0 = [触摸事件、performSelector:], sources1 = [基于端口的系统事件], observers = [CA动画回调、AutoreleasePool清理], timers = [NSTimer、performSelector:withObject:afterDelay:] }, ... ] } ``` #### 3. 运行流程 $$进入循环 \rightarrow 处理输入源 \rightarrow 进入休眠 \rightarrow 等待唤醒 \rightarrow 处理事件$$ **详细步骤**: 1. 通知Observer:即将进入Loop(`kCFRunLoopEntry`) 2. 通知Observer:即将处理Timer(`kCFRunLoopBeforeTimers`) 3. 通知Observer:即将处理Source(`kCFRunLoopBeforeSources`) 4. 处理Blocks 5. 处理Source0(非基于Port的事件) 6. 如果有Source1待处理,立即跳转处理 7. 通知Observer:即将休眠(`kCFRunLoopBeforeWaiting`) 8. 进入休眠(通过mach_msg系统调用) 9. 被以下事件唤醒: - Port-Based事件 - Timer到时间 - Runloop超时 - 被手动唤醒 10. 通知Observer:结束休眠(`kCFRunLoopAfterWaiting`) 11. 处理唤醒时收到的事件 12. 回到步骤2循环执行 #### 4. 关键应用 - 界面刷新:CADisplayLink与VSync信号同步 - 自动释放池:Runloop休眠前执行释放操作 - 事件响应:手势识别、屏幕触摸 - GCD调度:`dispatch_main_queue`通过Runloop执行 --- ### 三、两者关联 1. **消息处理**:Runloop驱动Runtime的消息分发 2. **对象生命周期**:Runloop控制AutoreleasePool的释放 3. **线程保活**:Runloop通过`CFRunLoopRun`维持线程存活,配合Runtime进行消息处理 ### 四、调试技巧 1. Runtime调试: ```bash # 打印类方法列表 (lldb) p [MyClass _methodDescription] ``` 2. Runloop观察: ```objective-c CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopAllActivities); ``` 理解这两个机制,可以帮助开发者: - 优化滚动流畅度(通过Runloop模式控制) - 诊断消息转发导致的崩溃 - 实现高性能的异步渲染 - 合理管理线程资源
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值