Runloop循环的代码,不是在主线程上运行的吗
Runloop 的这个无限循环代码,就是直接运行在主线程上的,这是核心关键 ——主线程的本质,就是执行 Runloop 无限循环的线程,二者是完全绑定的,不存在 “Runloop 循环在其他线程,主线程做别的事” 的情况。
一、先明确核心对应关系
iOS 中主线程的入口函数,最终就是启动 Runloop 的无限循环,伪代码的对应关系是:
// 主线程的入口(UIApplicationMain函数底层)
int main(int argc, char * argv[]) {
@autoreleasepool {
// 1. 初始化UIApplication、AppDelegate等核心对象
// 2. 启动「主线程的Runloop」,执行无限循环
CFRunLoopRunMain(); // 这个函数内部,就是Runloop的while(1)无限循环
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
简单说:主线程启动后,唯一的核心工作就是执行 Runloop 的无限循环,主线程的所有任务(UI 事件、渲染、定时器、网络回调等),都必须在这个循环体内部执行。
二、关键:“主线程执行 Runloop 循环” 的具体含义
你可以把主线程理解为一个 “执行代码的容器” ,把Runloop 的 while (1)理解为这个容器里永远在运行的 “主程序” ——
- 主线程被创建后,立刻加载并执行 Runloop 的无限循环代码;
- 这个循环代码霸占了主线程的执行权,主线程后续所有的操作,都是在这个循环体的内部被触发和执行的(比如处理触摸事件、执行帧渲染、调用你的业务代码);
- 只要 Runloop 的循环不退出,主线程就会一直存活(APP 前台运行时,主线程 Runloop 永不退出);如果循环退出,主线程就会执行完毕并销毁(APP 退出时才会发生)。
三、结合 “休眠” 再理解:主线程休眠 = Runloop 循环暂停
之前说的Runloop 让线程休眠,本质就是让主线程本身进入休眠态:
- 当 Runloop 执行到
mach_msg(内核休眠接口)时,主线程会从运行态切换为休眠态,此时主线程不再执行任何代码(包括 Runloop 的循环体),CPU 也会释放; - 当内核收到唤醒信号(如 CADisplayLink、触摸事件),会把主线程切回运行态,Runloop 的 while (1) 循环会继续执行后续的任务处理、渲染等逻辑。
一句话讲清:Runloop 的循环代码是主线程的 “核心程序”,主线程的生命周期和 Runloop 的循环绑定,Runloop 的休眠 / 唤醒就是主线程的休眠 / 唤醒。
四、再补一个易混点:主线程的任务,都在 Runloop 循环体内执行
你在主线程写的所有代码(比如viewDidLoad、按钮点击事件、dispatch_async(dispatch_get_main_queue(), ^{...})),都不是直接 “塞” 到主线程,而是被加入到 Runloop 的任务队列中,等待 Runloop 的循环体执行到对应阶段时,再被主线程执行。
举个例子,按钮点击的执行流程:
- 点击按钮,系统将 “点击事件” 加入主线程 Runloop 的事件队列;
- 内核唤醒主线程,Runloop 的 while (1) 循环继续执行;
- Runloop 从事件队列中取出 “点击事件”,让主线程执行你写的点击回调代码;
- 回调执行完毕,Runloop 检查是否有 UI 更新 / 其他任务,无则让主线程再次休眠。
最终总结
- Runloop 的无限循环直接运行在主线程,是主线程的核心执行代码;
- 主线程的生命周期与 Runloop 循环绑定,循环不退出,主线程就一直存活;
- Runloop 的 “休眠 / 唤醒” 就是主线程的休眠 / 唤醒,无任务时主线程休眠,有任务时被唤醒执行循环体;
- 主线程的所有任务(包括你的业务代码、UI 渲染、事件处理),都在 Runloop 循环体内部被执行。