Skip to content

01 - iOS 应用核心深度

目录

  1. 应用生命周期
  2. 启动流程深度分析
  3. 运行循环 RunLoop
  4. 沙盒机制
  5. 通知中心 KVO KVC
  6. 代理模式与闭包回调
  7. 单例模式
  8. 后台模式
  9. 面试考点汇总

1. 应用生命周期

iOS 应用生命周期深度分析:
┌─────────────────────────────────────────────────────────────────┐
│                                                                    │
│  生命周期流程:                                                     │
│  ┌───────────────────────────────────────────────────────────────┐   │
│  │  1. main() 函数                                                │
│  │  2. UIApplicationMain() 创建 UIApplication                    │
│  │  3. delegate 初始化                                            │
│  │  4. 创建 Scene / UIWindow                                     │
│  │  5. 设置 rootViewController                                   │
│  │  6. 进入 RunLoop                                               │
│  │                                                                │
│  │  生命周期方法(UIApplication):                               │
│  │  • application:didFinishLaunchingWithOptions:               │
│  │    → App 启动完成,可进行初始化                              │
│  │  • applicationDidBecomeActive:                               │
│  │    → App 成为前台活跃状态                                     │
│  │  • applicationWillResignActive:                             │
│  │    → App 即将进入后台                                        │
│  │  • applicationDidEnterBackground:                            │
│  │    → App 进入后台                                            │
│  │  • applicationWillEnterForeground:                          │
│  │    → App 即将进入前台                                        │
│  │  • applicationWillTerminate:                                │
│  │    → App 即将终止                                            │
│  └───────────────────────────────────────────────────────────────┘   │
│                                                                    │
│  SceneDelegate 生命周期(iOS 13+):                              │
│  ┌───────────────────────────────────────────────────────────────┐   │
│  │  • scene:willConnectToSession:options:                        │
│  │    → Scene 连接到 session                                    │
│  │  • sceneDidDisconnect:                                        │
│  │    → Scene 断开连接                                          │
│  │  • sceneDidBecomeActive:                                      │
│  │    → Scene 变为活跃状态                                      │
│  │  • sceneWillResignActive:                                    │
│  │    → Scene 即将失去焦点                                      │
│  │  • sceneWillEnterForeground:                                  │
│  │    → Scene 即将进入前台                                      │
│  │  • sceneDidEnterBackground:                                   │
│  │    → Scene 进入后台                                          │
│  └───────────────────────────────────────────────────────────────┘   │
│                                                                    │
│  生命周期对比(UIKit vs SwiftUI):                                │
│  ┌──────────────┬────────────────┬──────────────────┐              │   │
│  │ 阶段        │ UIKit           │ SwiftUI              │              │
│  ├──────────────┼────────────────┼──────────────────┤              │
│  │ 启动        │ AppDelegate     │ @main App struct   │              │
│  │ 场景连接    │ SceneDelegate   │ @main App struct   │              │
│  │ 视图加载    │ viewDidLoad     │ onAppear           │              │
│  │ 视图显示    │ viewWillAppear   │ —                  │              │
│  │ 视图消失    │ viewWillDisappear │ onDisappear        │              │
│  │ 内存警告    │ didReceiveMemoryWarning │ onDisappear    │              │
│  └──────────────┴────────────────┴──────────────────┘              │
│                                                                    │
│  生命周期注意事项:                                                │
│  ┌───────────────────────────────────────────────────────────────┐   │
│  │  • AppDelegate 和 SceneDelegate 共存                           │
│  │  • iOS 13+ 推荐使用 SceneDelegate                           │
│  │  • UIKit 应用也可使用 SceneDelegate                            │
│  │  • SwiftUI 应用使用 @main App struct                          │
│  │  • 生命周期方法中避免耗时操作                                 │
│  │  • 后台模式需要配置 Entitlements                              │
│  │  • 应用终止前需要保存数据                                      │
│  └───────────────────────────────────────────────────────────────┘   │
│                                                                    │
└─────────────────────────────────────────────────────────────────┘
*/

2. 启动流程深度分析

启动流程深度分析:
┌─────────────────────────────────────────────────────────────────┐
│                                                                    │
│  启动流程(详细):                                                 │
│  ┌───────────────────────────────────────────────────────────────┐   │
│  │  阶段 1: dyld(动态链接器)                                    │
│  │  • 加载 Mach-O 文件                                           │
│  │  • 链接依赖库(@rpath, @executable_path, @loader_path)       │
│  │  • 解析符号(函数、变量)                                      │
│  │  • 初始化(+load 方法调用)                                    │
│  │                                                                │
│  │  阶段 2: +load 初始化(静态)                                  │
│  │  • 所有类的 +load 方法按编译顺序调用                          │
│  │  • 所有分类的 +load 方法按编译顺序调用                        │
│  │  • 在 main() 之前调用                                          │
│  │  • 不能延迟(静态初始化)                                       │
│  │                                                                │
│  │  阶段 3: main() 函数                                          │
│  │  • 调用 UIApplicationMain()                                  │
│  │  • 创建 UIApplication 和 delegate                            │
│  │  • 设置主 Window 和 rootViewController                       │
│  │                                                                │
│  │  阶段 4: UIApplicationMain()                                  │
│  │  • 创建 UIApplication 实例                                    │
│  │  • 加载 Info.plist                                           │
│  │  • 创建 Scene / UIWindow                                    │
│  │  • 设置 delegate                                              │
│  │  • 启动 RunLoop                                               │
│  │                                                                │
│  │  阶段 5: RunLoop 启动                                          │
│  │  • 进入事件循环                                                │
│  │  • 监听 Source(输入源)                                       │
│  │  • 处理 Timer(定时器)                                        │
│  │  • 处理 GCD 任务                                              │
│  └───────────────────────────────────────────────────────────────┘   │
│                                                                    │
│  dyld 动态链接详解:                                              │
│  ┌───────────────────────────────────────────────────────────────┐   │
│  │  // dyld 工作流程                                              │
│  │  1. dyld 加载主程序 Mach-O 文件                               │
│  │  2. 递归加载依赖库(@rpath, @executable_path, @loader_path)  │
│  │  3. 解析未解析的符号                                           │
│  │  4. 调用 +load 方法(每个类和分类)                            │
│  │  5. 调用 +initialize 方法(懒加载)                            │
│  │  6. 跳转到 main()                                              │
│  │                                                                │
│  │  依赖路径优先级:                                              │
│  │  • @executable_path → 应用可执行文件路径                      │
│  │  • @loader_path → 加载者路径                                   │
│  │  • @rpath → 运行时路径搜索                                    │
│  │  • @provides → 提供库的符号                                  │
│  │  • @weak_framework → 弱依赖库                                 │
│  └───────────────────────────────────────────────────────────────┘   │
│                                                                    │
│  +load vs +initialize 对比:                                     │
│  ┌──────────────┬──────────────────┬──────────────────┐              │
│  │ 特性        │ +load            │ +initialize        │              │
│  ├──────────────┼──────────────────┼──────────────────┤              │
│  │ 调用时机    │ dyld 加载时       │ 首次使用类时       │              │
│  │ 线程安全    │ ✅ 安全           │ ✅ 安全(dispatch_once)│             │
│  │ 调用顺序    │ 按编译顺序          │ 首次使用            │              │
│  │ 延迟初始化  │ ❌ 不延迟           │ ✅ 可延迟           │              │
│  │ 推荐用途    │ 静态初始化          │ 懒加载初始化        │              │
│  └──────────────┴──────────────────┴──────────────────┘              │
│                                                                    │
│  启动优化策略:                                                   │
│  ┌───────────────────────────────────────────────────────────────┐   │
│  │  • 减少 dyld 链接时间(合并 framework、减少动态库)             │
│  │  • 减少 +load 方法(避免静态初始化)                         │
│  │  • 延迟初始化(非关键功能延迟到 main 之后)                     │
│  │  • 预编译头(PCH)减少编译时间                                │
│  │  • 主线程异步化(减少主线程工作)                              │
│  │  • 使用 LaunchScreen 减少首屏加载时间                          │
│  │  • 减少 -init 初始化函数                                       │
│  │  • 图片懒加载                                                 │
│  │  • JSON 预编译                                               │
│  └───────────────────────────────────────────────────────────────┘   │
│                                                                    │
│  启动时间测量:                                                   │
│  ┌───────────────────────────────────────────────────────────────┐   │
│  │  • Instruments(Time Profiler)                              │
│  │  • NSLog 手动计时                                             │
│  │  • DYLD_STATS 环境变量                                      │
│  │  • NSTimeInterval 计时                                       │
│  │                                                                │
│  │  冷启动时间分解:                                              │
│  │  • dyld 加载 + 链接:~300ms                                   │
│  │  • +load 初始化:~50ms                                        │
│  │  • main() + UIApplicationMain:~100ms                        │
│  │  • Scene/Window 创建:~50ms                                   │
│  │  • rootViewController 加载:~100ms                            │
│  │  • 首屏渲染 + 数据加载:~300ms                                 │
│  │  ─────────────────────────────────────────────────────       │
│  │  总冷启动时间:~900ms(理想)→ 2-5s(实际)                   │
│  └───────────────────────────────────────────────────────────────┘   │
│                                                                    │
└─────────────────────────────────────────────────────────────────┘
*/

3. 运行循环 RunLoop

RunLoop 深度分析:
┌─────────────────────────────────────────────────────────────────┐
│  RunLoop 的核心:                                                   │
│  ┌───────────────────────────────────────────────────────────────┐   │
│  │  RunLoop 的本质:                                             │
│  │  • 事件处理循环(Event Loop)                                  │
│  │  • 管理事件源(Source)、定时器(Timer)、观察者(Observer)   │
│  │  • 保持线程存活                                               │
│  │  • 自动管理内存(AutoreleasePool)                          │
│  │                                                                │
│  │  RunLoop 结构:                                               │
│  │  ┌─────────────────────────────────────────────────────────┐   │
│  │  │  CFRunLoopRef                                                │   │
│  │  │  ├── CFRunLoopMode                                           │   │
│  │  │  │  ├── CFSocketPort(网络事件)                            │   │
│  │  │  │  ├── GCD(DispatchSource)                              │   │
│  │  │  │  ├── Timer(NSTimer)                                  │   │
│  │  │  │  └── Observer(输入源/定时器/端口)                     │   │
│  │  │  └── CFRunLoopMode                                           │   │
│  │  └─────────────────────────────────────────────────────────┘   │
│  │                                                                │
│  │  RunLoop 模式(Modes):                                     │
│  │  ┌───────────────────────────────────────────────────────────┐   │
│  │  │  • NSDefaultRunLoopMode(默认模式)                       │
│  │  │  • NSRunLoopCommonModes(常用模式,包含所有模式)        │
│  │  │  • UITrackingRunLoopMode(触摸跟踪模式)                │
│  │  │  • NSRunLoopInputMode(输入模式)                         │
│  │  │  • NSRunLoopModalMode(模态模式)                        │
│  │  └───────────────────────────────────────────────────────────┘   │
│  │                                                                │
│  │  RunLoop 工作流程:                                           │
│  │  ┌────────────────────────────────────────────────────────┐   │
│  │  │  1. 准备(prepare)                                      │
│  │  │  2. 处理源(handle sources)                             │
│  │  │  3. 处理定时器(handle timers)                          │
│  │  │  4. 处理观察者(handle observers)                       │
│  │  │  5. 休眠(wait)                                        │
│  │  │  6. 唤醒(wakeup)                                       │
│  │  │  7. 通知(notify)                                       │
│  │  │  8. 退出(exit)                                        │
│  │  └────────────────────────────────────────────────────────┘   │
│  │                                                                │
│  │  RunLoop 应用场景:                                           │
│  │  ┌───────────────────────────────────────────────────────────┐   │
│  │  │  • 懒加载(延迟初始化)                                     │
│  │  │  • 自动释放池(内存管理)                                  │
│  │  │  • 保持线程存活                                             │
│  │  │  • 异步任务(GCD + RunLoop)                              │
│  │  │  • Timer 管理                                               │
│  │  │  • 网络事件处理                                            │
│  │  │  • 触摸事件处理                                            │
│  │  └───────────────────────────────────────────────────────────┘   │
│  │                                                                │
│  │  RunLoop 与 Timer 的关系:                                   │
│  │  ┌───────────────────────────────────────────────────────────┐   │
│  │  │  • Timer 依赖 RunLoop 模式                                  │
│  │  │  • Timer 在 NSDefaultRunLoopMode 下工作                     │
│  │  │  • 当用户滚动时,RunLoop 切换到 UITrackingRunLoopMode        │
│  │  │  • Timer 在 UITrackingRunLoopMode 下暂停                   │
│  │  │  • 解决方法:将 Timer 添加到 NSRunLoopCommonModes            │
│  │  │                                                            │
│  │  │  // 解决方法:                                               │
│  │  │  let timer = Timer(timeInterval: 1.0, target: self,         │
│  │  │      selector: #selector(timerFired), userInfo: nil,        │
│  │  │      repeats: true)                                        │
│  │  │  RunLoop.current.add(timer, forMode: .commonModes)         │
│  │  └───────────────────────────────────────────────────────────┘   │
│  │                                                                │
│  │  RunLoop 性能分析:                                           │
│  │  ┌───────────────────────────────────────────────────────────┐   │
│  │  │  • 主线程 RunLoop 持续运行                                  │
│  │  │  • 子线程需手动管理                                          │
│  │  │  • 主线程卡顿:RunLoop 被阻塞                              │
│  │  │  • 主线程卡顿检测:CADisableMinimumFrameDuration           │
│  │  │  • 主线程卡顿修复:减少主线程工作量                        │
│  │  │  • 主线程卡顿修复:异步处理                               │
│  │  └───────────────────────────────────────────────────────────┘   │
│  │                                                                │
│  │  总结:                                                     │
│  │  • RunLoop 是事件循环的核心机制                                │
│  │  • 管理事件源、定时器、观察者                                  │
│  │  • 保持线程存活                                               │
│  │  • 自动管理内存(AutoreleasePool)                          │
│  │  • 主线程 RunLoop 持续运行                                   │
│  │  • Timer 依赖 RunLoop 模式                                   │
│  │  • 主线程卡顿:RunLoop 被阻塞                               │
│  └───────────────────────────────────────────────────────────────┘   │
│                                                                    │
│  RunLoop 与 GCD 的关系:                                          │
│  ┌──────────────┬──────────────────┬──────────────────┐              │
│  │ 特性        │ RunLoop           │ GCD                │              │
│  ├──────────────┼──────────────────┼──────────────────┤              │
│  │ 本质        │ 事件循环            │ 任务调度            │              │
│  │ 管理        │ 事件源/定时器/观察者 │ 队列/线程池        │              │
│  │ 线程        │ 手动管理            │ 自动管理            │              │
│  │ 模式        │ Mode(模式)        │ Priority(优先级)  │              │
│  │ 适用        │ 事件处理、Timer     │ 并发任务、异步处理    │              │
│  └──────────────┴──────────────────┴──────────────────┘              │
│                                                                    │
└─────────────────────────────────────────────────────────────────┘
*/

4. 沙盒机制

沙盒机制深度分析:
┌─────────────────────────────────────────────────────────────────┐
│  iOS 沙盒结构:                                                   │
│  ┌───────────────────────────────────────────────────────────────┐   │
│  │  /Applications/                                                │
│  │  ├── YourApp.app/                                              │
│  │  │  ├── Info.plist                                             │
│  │  │  ├── YourApp(可执行文件)                                  │
│  │  │  ├── Resources/(资源文件)                                 │
│  │  │  └── Frameworks/(动态库)                                 │
│  │  └── ...                                                      │
│  │                                                                │
│  │  /private/var/containers/Bundle/Application/                  │
│  │  └── YourApp.app/                                              │
│  │                                                                │
│  │  /private/var/mobile/Containers/Data/Application/              │
│  │  ├── Documents/(用户数据,iTunes 同步)                      │
│  │  ├── Library/                                                  │
│  │  │  ├── Caches/(缓存数据,可清除)                         │
│  │  │  ├── Preferences/(UserDefaults)                        │
│  │  │  └── Application Support/(应用数据)                    │
│  │  └── tmp/(临时文件,可被系统清理)                         │
│  └───────────────────────────────────────────────────────────────┘   │
│                                                                    │
│  沙盒权限:                                                       │
│  ┌───────────────────────────────────────────────────────────────┐   │
│  │  • 应用只能访问自己的沙盒目录                                  │
│  │  • 不能访问其他应用的沙盒                                      │
│  │  • 不能访问系统目录                                            │
│  │  • 文件权限:rwx------(应用所有者权限)                      │
│  │  • 目录权限:rwxr-xr-x(其他应用只读)                       │
│  │  • 网络权限:需要通过 Entitlements 配置                       │
│  │  • 设备权限:需要通过 Entitlements 配置                       │
│  │  • iCloud 权限:需要通过 Entitlements 配置                    │
│  │                                                                │
│  │  目录用途总结:                                                │
│  │  • Documents:用户数据(需备份)                              │
│  │  ├── Library/Caches:缓存数据(可清除)                      │
│  │  ├── Library/Preferences:用户偏好设置                      │
│  │  └── tmp:临时文件(可被系统清理)                           │
│  └───────────────────────────────────────────────────────────────┘   │
│                                                                    │
│  沙盒最佳实践:                                                  │
│  ┌───────────────────────────────────────────────────────────────┐   │
│  │  • 使用 FileManager.default 获取路径                          │
│  │  • 使用 UserDefaults 存储偏好设置                            │
│  │  • 使用 CoreData/SQLite 存储结构化数据                      │
│  │  • 使用 Keychain 存储敏感数据                                 │
│  │  • 使用 FileManager 进行文件操作                              │
│  │  • 避免在沙盒外读写数据                                       │
│  │  • 注意文件权限                                               │
│  │  • 注意 iCloud 同步                                            │
│  └───────────────────────────────────────────────────────────────┘   │
│                                                                    │
└─────────────────────────────────────────────────────────────────┘
*/

5. 通知中心 KVO KVC

通知中心 KVO KVC 深度分析:
┌─────────────────────────────────────────────────────────────────┐
│  NotificationCenter:                                             │
│  ┌───────────────────────────────────────────────────────────────┐   │
│  │  // NotificationCenter 使用                                        │
│  │  // 发送通知                                                    │
│  │  NotificationCenter.default.post(name: .someNotification,      │
│  │      object: self, userInfo: ["key": "value"])                │
│  │                                                                │
│  │  // 接收通知                                                    │
│  │  NotificationCenter.default.addObserver(                      │
│  │      self,                                                     │
│  │      selector: #selector(handleNotification),                  │
│  │      name: .someNotification,                                  │
│  │      object: nil                                               │
│  │  )                                                            │
│  │                                                                │
│  │  // 移除观察者                                                  │
│  │  NotificationCenter.default.removeObserver(self)              │
│  │                                                                │
│  │  NotificationCenter 原理:                                   │
│  │  • 观察者模式实现                                              │
│  │  • 基于 NSHashTable(弱引用)                                  │
│  │  • 线程安全(NSLock)                                          │
│  │  • 观察者按添加顺序通知                                          │
│  │  • 通知对象可以为 nil(广播模式)                              │
│  │  • userInfo 用于传递额外信息                                    │
│  │                                                                │
│  │  NotificationCenter 优缺点:                                 │
│  │  ┌──────────────┬──────────────────┬──────────────────┐              │
│  │  │ 优点        │ 缺点            │                  │              │
│  │  ├──────────────┼──────────────────┼──────────────────┤              │
│  │  │ 解耦          │ 内存泄漏风险    │                  │              │
│  │  │ 灵活          │ 类型不安全       │                  │              │
│  │  │ 广播          │ 调试困难          │                  │              │
│  │  │ 解耦          │ 内存泄漏风险    │                  │              │
│  │  │ 灵活          │ 类型不安全       │                  │              │
│  │  │ 广播          │ 调试困难          │                  │              │
│  │  │ 解耦          │ 内存泄漏风险    │                  │              │
│  │  │ 灵活          │ 类型不安全       │                  │              │
│  │  │ 广播          │ 调试困难          │                  │              │
│  │  └──────────────┴──────────────────┴──────────────────┘              │
│  │                                                                │
│  │  NotificationCenter 使用注意:                               │
│  │  • 观察者模式实现                                                │
│  │  • 基于 NSHashTable(弱引用)                                  │
│  │  • 线程安全(NSLock)                                          │
│  │  • 观察者按添加顺序通知                                          │
│  │  • 通知对象可以为 nil(广播模式)                              │
│  │  • userInfo 用于传递额外信息                                    │
│  │  • 移除观察者(防止内存泄漏)                                  │
│  │  • 使用 Notifications 代替(类型安全)                        │
│  └───────────────────────────────────────────────────────────────┘   │
│                                                                    │
│  KVO(Key-Value Observing):                                    │
│  ┌───────────────────────────────────────────────────────────────┐   │
│  │  // KVO 原理                                                  │
│  │  • 动态替换 isa 指针                                             │
│  │  • 创建 NSKeyValueObserver 子类                                │
│  │  • 拦截 valueForKey/setValueForKey 调用                       │
│  │  • 通知观察者                                                   │
│  │                                                                │
│  │  // KVO 使用                                                    │
│  │  object.addObserver(self, forKeyPath: "propertyName",         │
│  │      options: .new, context: nil)                             │
│  │                                                                │
│  │  // KVO 注意事项                                                │
│  │  • 移除观察者(防止内存泄漏)                                  │
│  │  • 使用 Swift KeyPath(类型安全)                            │
│  │  • 使用 Combine(现代替代方案)                               │
│  │  • 使用 KVO 替代方案(Delegate/Callback)                   │
│  │                                                                │
│  │  KVO vs NotificationCenter:                                │
│  │  ┌──────────────┬──────────────────┬──────────────────┐              │
│  │  │ 特性        │ KVO             │ NotificationCenter   │              │
│  │  ├──────────────┼──────────────────┼──────────────────┤              │
│  │  │ 解耦        │ 低                │ 高                │              │
│  │  │ 类型安全    │ 高                │ 低                │              │
│  │  │ 适用        │ 属性变化            │ 事件通知            │              │
│  │  │ 性能        │ 中                │ 高                │              │
│  │  └──────────────┴──────────────────┴──────────────────┘              │
│  │                                                                │
│  │  KVC(Key-Value Coding):                                   │
│  │  ┌──────────────┬──────────────────┬──────────────────┐              │
│  │  │ 特性        │ KVC              │                  │              │
│  │  ├──────────────┼──────────────────┼──────────────────┤              │
│  │  │ 本质        │ 动态键值访问        │                  │              │
│  │  │ 原理        │ NSDictionary/NSMutableDictionary 映射     │              │
│  │  │ 使用        │ valueForKey/setValueForKey                 │              │
│  │  │ 类型安全    │ 低(字符串键)      │                  │              │
│  │  │ 适用        │ 动态数据绑定        │                  │              │
│  │  └──────────────┴──────────────────┴──────────────────┘              │
│  │                                                                │
│  │  KVC 使用:                                                  │
│  │  ┌───────────────────────────────────────────────────────────┐   │
│  │  │  // 获取值                                                    │
│  │  │  let value = object.value(forKey: "propertyName") as? String  │
│  │  │                                                              │
│  │  │  // 设置值                                                    │
│  │  │  object.setValue("new value", forKey: "propertyName")       │
│  │  │                                                              │
│  │  │  // KVC 注意事项                                              │
│  │  │  • 键路径不存在时调用 setValue:forUndefinedKey:(可覆盖)    │
│  │  │  • 线程安全:需手动管理                                       │
│  │  │  • 类型安全:低(字符串键)                                    │
│  │  │  • 推荐使用 Combine/SwiftUI 替代                            │
│  │  └───────────────────────────────────────────────────────────┘   │
│  │                                                                │
│  └───────────────────────────────────────────────────────────────┘   │
│                                                                    │
└─────────────────────────────────────────────────────────────────┘
*/

6. 代理模式与闭包回调

代理模式与闭包回调深度分析:
┌─────────────────────────────────────────────────────────────────┐
│  代理模式:                                                       │
│  ┌───────────────────────────────────────────────────────────────┐   │
│  │  // 代理模式本质:                                               │
│  │  • 协议驱动的设计模式                                          │
│  │  • 委托方持有代理(weak)                                      │
│  │  • 代理实现协议方法                                            │
│  │  • 委托方通过代理方法通知代理                                    │
│  │                                                                │
│  │  代理模式实现:                                                │
│  │  ┌──────────────────────────────────────────────────────────┐   │
│  │  │  protocol MyDelegate: AnyObject {                         │
│  │  │      func didChange(value: Int)                            │
│  │  │  }                                                        │
│  │  │                                                            │
│  │  │  class MyClass {                                          │
│  │  │      weak var delegate: MyDelegate?                      │
│  │  │                                                            │
│  │  │      func doSomething() {                               │
│  │  │          // ...                                         │
│  │  │          delegate?.didChange(value: newValue)             │
│  │  │      }                                                    │
│  │  │  }                                                        │
│  │  └──────────────────────────────────────────────────────────┘   │
│  │                                                                │
│  │  代理模式 vs 闭包回调:                                      │
│  │  ┌──────────────┬──────────────────┬──────────────────┐              │
│  │  │ 特性        │ 代理模式          │ 闭包回调             │              │
│  │  ├──────────────┼──────────────────┼──────────────────┤              │
│  │  │ 解耦        │ 高                │ 中                │              │
│  │  │ 类型安全    │ 高(协议)        │ 中(泛型)        │              │
│  │  │ 适用        │ 多方法、多事件    │ 单事件、简单回调    │              │
│  │  │ 生命周期    │ 需手动管理         │ 可选强/弱捕获      │              │
│  │  │ 性能        │ 中                │ 高                │              │
│  │  │ 推荐        │ 多事件             │ 单事件             │              │
│  │  └──────────────┴──────────────────┴──────────────────┘              │
│  │                                                                │
│  │  代理模式最佳实践:                                             │
│  │  • 代理协议使用 weak 修饰                                      │
│  │  • 代理协议名称以 Delegate 结尾                                │
│  │  • 代理方法使用 @objc optional 可选方法                      │
│  │  • 代理方法命名以 did/will 开头                                │
│  │  • 代理方法参数使用有意义的名称                                │
│  │  • 使用委托模式代替继承                                        │
│  │  • 使用协议扩展提供默认实现                                    │
│  └───────────────────────────────────────────────────────────────┘   │
│                                                                    │
│  闭包回调:                                                       │
│  ┌───────────────────────────────────────────────────────────────┐   │
│  │  // 闭包回调实现                                              │
│  │  class MyClass {                                             │
│  │      func doSomething(completion: @escaping (Result<Data, Error>) -> Void) {  │
│  │          // ...                                              │
│  │          completion(.success(data))                            │
│  │      }                                                        │
│  │  }                                                            │
│  │                                                                │
│  │  闭包回调最佳实践:                                          │
│  │  • 使用 @escaping 修饰闭包                                  │
│  │  • 使用 Result 包装返回值                                    │
│  │  • 使用 [weak self] 捕获 self                              │
│  │  • 使用 MainActor.run 在主线程回调                          │
│  │  • 使用 async/await 替代闭包(现代 Swift)                   │
│  │  • 避免内存泄漏(正确捕获 self)                            │
│  │  • 使用 CompletionHandler 命名(可选)                       │
│  └───────────────────────────────────────────────────────────────┘   │
│                                                                    │
└─────────────────────────────────────────────────────────────────┘
*/

7. 单例模式

单例模式深度分析:
┌─────────────────────────────────────────────────────────────────┐
│  单例模式实现:                                                   │
│  ┌───────────────────────────────────────────────────────────────┐   │
│  │  // 1. Swift 静态实例(推荐 ✅)                              │
│  │  final class Singleton {                                      │
│  │      static let shared = Singleton()                          │
│  │      private init() { }                                        │
│  │  }                                                            │
│  │                                                                │
│  │  // 2. Objective-C 实现                                       │
│  │  @interface Singleton : NSObject                              │
│  │  @property (nonatomic, class, readonly) Singleton *shared;   │
│  │  @end                                                        │
│  │  @implementation Singleton                                   │
│  │  + (instancetype)shared {                                     │
│  │      static Singleton *_shared = nil;                        │
│  │      static dispatch_once_t onceToken;                       │
│  │      dispatch_once(&onceToken, ^{                           │
│  │          _shared = [[Singleton alloc] init];                  │
│  │      });                                                      │
│  │      return _shared;                                          │
│  │  }                                                           │
│  │  @end                                                       │
│  │                                                                │
│  │  // 3. Swift 线程安全单例(GCD)                              │
│  │  class Singleton {                                            │
│  │      static let shared: Singleton = {                       │
│  │          let instance = Singleton()                          │
│  │          return instance                                      │
│  │      }()                                                      │
│  │  }                                                            │
│  │                                                                │
│  │  单例注意事项:                                               │
│  │  • 使用 final 防止继承                                        │
│  │  • init 设为 private                                          │
│  │  • 使用 class 而非 static(避免循环依赖)                   │
│  │  • 内存泄漏:单例持有 strong 循环                           │
│  │  • 线程安全:dispatch_once 或 GCD                            │
│  │  • 内存泄漏:单例持有 strong 循环                           │
│  │  • 线程安全:dispatch_once 或 GCD                            │
│  │                                                                │
│  │  单例优缺点:                                               │
│  │  ┌──────────────┬──────────────────┬──────────────────┐              │
│  │  │ 优点        │ 缺点            │                  │              │
│  │  ├──────────────┼──────────────────┼──────────────────┤              │
│  │  │ 全局唯一      │ 内存泄漏风险    │                  │              │
│  │  │ 线程安全      │ 难以测试        │                  │              │
│  │  │ 访问快速      │ 单点故障        │                  │              │
│  │  │ 简化访问      │ 依赖隐式       │                  │              │
│  │  └──────────────┴──────────────────┴──────────────────┘              │
│  │                                                                │
│  │  单例使用场景:                                               │
│  │  • 全局配置管理器                                              │
│  │  • 网络请求管理器                                              │
│  │  • 缓存管理器                                                  │
│  │  • 数据库管理器                                                │
│  │  • 用户管理器                                                  │
│  │  • 状态管理器                                                  │
│  └───────────────────────────────────────────────────────────────┘   │
│                                                                    │
└─────────────────────────────────────────────────────────────────┘
*/

8. 后台模式

后台模式深度分析:
┌─────────────────────────────────────────────────────────────────┐
│  iOS 后台模式:                                                   │
│  ┌───────────────────────────────────────────────────────────────┐   │
│  │  后台模式类型:                                                │
│  │  • BGTaskScheduler(后台任务调度器)                          │
│  │  • BGAppRefreshTask(App 刷新任务)                         │
│  │  • BGMaintenanceTask(维护任务)                            │
│  │  • BGProcessingTask(处理任务)                            │
│  │  • backgroundFetch(后台拉取)                               │
│  │  • remoteNotification(远程通知)                           │
│  │  • audio(音频播放)                                          │
│  │  • location(位置服务)                                       │
│  │  • voip(VoIP)                                              │
│  │  • externalAccessory(外部配件)                            │
│  │                                                                │
│  │  后台任务注册:                                              │
│  │  ┌──────────────────────────────────────────────────────────┐   │
│  │  │  BGTaskScheduler.shared.register(                       │
│  │  │      forTaskWithIdentifier: "com.app.refresh",             │
│  │  │      using: nil) { task in                                │
│  │  │      self.handleAppRefresh(task: task as! BGAppRefreshTask)  │
│  │  │  }                                                       │
│  │  │                                                            │
│  │  │  // 调度后台任务                                            │
│  │  │  let task = BGAppRefreshTask(identifier: "com.app.refresh")  │
│  │  │  task.expirationHandler = {                               │
│  │  │      // 任务即将过期                                        │
│  │  │  }                                                        │
│  │  │  BGTaskScheduler.shared.submit(task)                      │
│  │  │                                                            │
│  │  │  // 后台任务执行                                            │
│  │  │  func handleAppRefresh(task: BGAppRefreshTask) {          │
│  │  │      // 执行后台任务                                        │
│  │  │      task.setTaskCompleted(success: true)                 │
│  │  │  }                                                        │
│  │  └──────────────────────────────────────────────────────────┘   │
│  │                                                                │
│  │  后台模式配置:                                              │
│  │  • Info.plist 添加 UIBackgroundModes                        │
│  │  • Entitlements 配置后台权限                                 │
│  │  • 后台任务需要用户授权                                       │
│  │  • 后台任务有执行时间限制(约 30s)                          │
│  │  • 后台任务需要合理的调度频率                                 │
│  │  • 后台任务需要处理网络请求                                   │
│  │  • 后台任务需要处理数据库操作                                 │
│  │  • 后台任务需要处理文件操作                                   │
│  │  • 后台任务需要处理图片加载                                   │
│  │  • 后台任务需要处理视频处理                                   │
│  │                                                                │
│  │  后台模式最佳实践:                                           │
│  │  • 使用 BGTaskScheduler 注册任务                           │
│  │  • 使用 BGAppRefreshTask 进行 App 刷新                      │
│  │  • 使用 BGMaintenanceTask 进行维护任务                       │
│  │  • 使用 BGProcessingTask 进行长时间任务                       │
│  │  • 使用 backgroundFetch 进行后台拉取                        │
│  │  • 使用 remoteNotification 进行远程通知                      │
│  │  • 使用 audio 进行音频播放                                    │
│  │  • 使用 location 进行位置服务                                 │
│  │  • 使用 voip 进行 VoIP 通话                                 │
│  │  • 使用 externalAccessory 进行外部配件                      │
│  └───────────────────────────────────────────────────────────────┘   │
│                                                                    │
└─────────────────────────────────────────────────────────────────┘
*/

9. 面试考点汇总

高频面试题

Q1: iOS 应用生命周期是怎样的?

  • 启动流程:main() → UIApplicationMain() → delegate → Scene → UIWindow → rootVC
  • 生命周期方法:didFinishLaunching → didBecomeActive → applicationDidEnterBackground
  • iOS 13+ 使用 SceneDelegate 管理场景

Q2: 启动流程中的 dyld 作用?

  • 加载 Mach-O 文件、链接依赖库、解析符号
  • 调用 +load 方法(静态初始化)
  • 调用 +initialize 方法(懒加载)
  • 优化:减少动态库、预编译头、延迟初始化

Q3: RunLoop 的核心作用?

  • 事件处理循环(Event Loop)
  • 管理事件源、定时器、观察者
  • 保持线程存活
  • 自动管理内存(AutoreleasePool)
  • 主线程 RunLoop 持续运行

Q4: 沙盒机制是什么?

  • 每个应用有独立的文件系统
  • 不能访问其他应用的沙盒
  • 目录:Documents、Library、tmp
  • 文件权限:rwx------(应用所有者)

Q5: 单例模式的实现?

  • Swift:static let shared = Singleton()
  • Objective-C:dispatch_once 线程安全
  • 注意事项:final、private init、线程安全
  • 单例使用场景:全局配置、网络管理器、缓存管理器

Q6: 后台模式有哪些?

  • BGTaskScheduler(后台任务调度器)
  • BGAppRefreshTask(App 刷新)
  • BGMaintenanceTask(维护任务)
  • BGProcessingTask(长时间任务)
  • backgroundFetch(后台拉取)
  • remoteNotification(远程通知)


2.5 启动流程底层原理深度剖析

2.5.1 dyld 启动链完整分析

dyld 启动链完整流程(iOS 17+):
┌─────────────────────────────────────────────────────────────────────┐
│  Level 0: dyld 入口                                                   │
│  ┌─────────────────────────────────────────────────────────────────┐
│  │  __dyld_start (汇编入口点)                                       │
│  │  ├── 创建初始栈帧                                                   │
│  │  ├── 保存 argc/argv/environp                                     │
│  │  └── 跳转到 dyldbootstrap::start                                │
│  └─────────────────────────────────────────────────────────────────┘
│                                                                      │
│  Level 1: dyldbootstrap 初始化                                       │
│  ┌─────────────────────────────────────────────────────────────────┐
│  │  dyldbootstrap::start()                                           │
│  │  ├── getExecutablePath() → 获取可执行文件路径                       │
│  │  ├── _mh_execute_header → 获取 Mach-O header                     │
│  │  ├── dyld::initializeMainStack() → 重建堆栈                       │
│  │  └── dyld::runInitializers() → 调用 __dyld_section               │
│  └─────────────────────────────────────────────────────────────────┘
│                                                                      │
│  Level 2: dyld 主初始化                                                │
│  ┌─────────────────────────────────────────────────────────────────┐
│  │  dyld::initializeMain()                                           │
│  │  ├── loadMainExecutable() → 加载主程序 Mach-O                      │
│  │  ├── mapSections() → 映射代码段/数据段到内存                        │
│  │  ├── bindSymbols() → 绑定符号引用                                 │
│  │  ├── loadAllDependentLibraries() → 递归加载依赖库                   │
│  │  └── callInitializers() → 调用所有 +load 方法                     │
│  └─────────────────────────────────────────────────────────────────┘
│                                                                      │
│  Level 3: Mach-O 加载器                                                │
│  ┌─────────────────────────────────────────────────────────────────┐
│  │  Mach-O 文件格式:                                                  │
│  │  ┌────────────────────────────────────────────────────────────┐  │
│  │  │  Magic (0xfeedface/0xfeedfacf)                              │  │
│  │  │  CPU Type (ARM64)                                         │  │
│  │  │  CPU Subtype                                               │  │
│  │  │  File Type (MH_EXECUTE)                                   │  │
│  │  │  Load Commands Count                                       │  │
│  │  │  Load Commands → [LC_SEGMENT_64, LC_SYMTAB, LC_DYSYMTAB...] │
│  │  │  Text Segment → 代码段 (.text, .text._dispatch)             │  │
│  │  │  Data Segment → 数据段 (.data, .cstring)                    │  │
│  │  │  BSS Segment → 未初始化数据段                                │  │
│  │  │  Symbol Table → 符号表                                      │  │
│  │  │  String Table → 字符串表                                     │  │
│  │  └────────────────────────────────────────────────────────────┘  │
│  └─────────────────────────────────────────────────────────────────┘
│                                                                      │
│  Level 4: 依赖库加载顺序                                                │
│  ┌─────────────────────────────────────────────────────────────────┐
│  │  dyld3 的依赖库加载(iOS 12+ 使用 dyld3)                        │
│  │  ├── dyldA::link() → 动态链接器核心                              │
│  │  ├── GraphResolver::resolve() → 符号解析                         │
│  │  ├── ImageLoader::link() → 链接单个 Image                        │
│  │  ├── ImageLoader::bind() → 绑定引用                              │
│  │  ├── ImageLoader::postLoad() → 后处理                            │
│  │  └── ImageLoader::initialize() → 调用 +initialize               │
│  │                                                                      │
│  │  依赖库搜索路径优先级(dyld3):                                    │
│  │  1. @executable_path → /Applications/YourApp.app/                │
│  │  2. @loader_path → 当前 Image 的加载路径                           │
│  │  3. @rpath → 运行时路径(从 Info.plist 读取)                       │
│  │  4. @framework_path → 系统 Framework 路径                         │
│  │  5. DYLD_FRAMEWORK_PATH → 环境变量                               │
│  │  6. DYLD_LIBRARY_PATH → 库搜索路径                                │
│  └─────────────────────────────────────────────────────────────────┘
│                                                                      │
│  Level 5: Mach-O 加载命令详解                                           │
│  ┌─────────────────────────────────────────────────────────────────┐
│  │  LC_LOAD_DYLIB          → 动态链接库                            │
│  │  LC_LOAD_UPWARD_DYLIB   → 向上加载依赖库                        │
│  │  LC_ID_DYLIB            → 库身份标识                            │
│  │  LC_SEGMENT_64          → 64位段映射                            │
│  │  LC_SYMTAB              → 符号表                                │
│  │  LC_DYSYMTAB            → 动态符号表                            │
│  │  LC_LOAD_WEAK_DYLIB     → 弱链接动态库                          │
│  │  LC_CODE_SIGNATURE      → 代码签名                            │
│  │  LC_FUNCTION_STARTS     → 函数起始地址                          │
│  │  LC_MAIN                → main 入口地址(替代 LC_UNIXTHREAD)     │
│  │  LC_SOURCE_VERSION      → 源码版本                            │
│  │  LC_VERSION_MIN_IPHONEOS → iOS 最低版本要求                      │
│  │  LC_RPATH               → 运行时路径搜索                        │
│  │  LC_ENCRYPTION_INFO_64  → 代码加密信息                          │
│  │  LC_BUILD_VERSION       → 构建版本(toolset/sdk)                │
│  └─────────────────────────────────────────────────────────────────┘
│                                                                      │
│  Level 6: 符号绑定完整流程                                               │
│  ┌─────────────────────────────────────────────────────────────────┐
│  │  符号绑定阶段:                                                       │
│  │  ├── Non-lazy binding → 非懒绑定(函数指针,启动时绑定)              │
│  │  ├── Lazy binding → 懒绑定(函数指针,首次调用时绑定)               │
│  │  └── Lazy binding stub → 懒绑定桩代码(调用 __dyld_lazy_bind)    │
│  │                                                                      │
│  │  符号表结构(LC_SYMTAB):                                         │
│  │  ├── symtab.symoff → 符号表偏移                                  │
│  │  ├── symtab.nsyms → 符号数量                                    │
│  │  ├── strtab.stroff → 字符串表偏移                                │
│  │  └── strtab.strsize → 字符串表大小                               │
│  │                                                                      │
│  │  符号类型(n_type 字段):                                         │
│  │  ├── N_SECT → 段内符号                                            │
│  │  ├── N_PBUD → 未绑定动态符号                                      │
│  │  ├── N_ABS → 绝对符号                                             │
│  │  └── N_INDR → 间接符号                                            │
│  └─────────────────────────────────────────────────────────────────┘
│                                                                      │
│  冷启动 vs 热启动时间对比(iPhone 15 Pro):                            │
│  ┌───────────┬──────────────┬──────────────┬──────────────┐          │
│  │ 阶段      │ 冷启动(ms)   │ 热启动(ms)   │ 增量(ms)     │          │
│  ├──────────┼──────────────┼──────────────┼──────────────┤          │
│  │ dyld加载  │ 180          │ 12           │ +168         │          │
│  │ 符号链接  │ 120          │ 8            │ +112         │          │
│  │ +load     │ 45           │ 3            │ +42          │          │
│  │ +init     │ 60           │ 2            │ +58          │          │
│  │ main()    │ 30           │ 2            │ +28          │          │
│  │ UI创建    │ 150          │ 20           │ +130         │          │
│  │ 首屏渲染  │ 200          │ 30           │ +170         │          │
│  │ 网络请求  │ 500          │ 400          │ +100         │          │
│  │ ────────  │ ────────────  │ ────────────  │ ────────────  │          │
│  │ 总计      │ 1285         │ 497          │ +788         │          │
│  └───────────┴──────────────┴──────────────┴──────────────┘          │
│                                                                      │
│  冷启动时间优化策略(实测数据):                                       │
│  ┌─────────────────────────────────────────────────────────────────┐
│  │  优化策略                          │ 减少时间(ms)  │ 优先级  │
│  │  ├── 减少 dyld 链接时间(合并 framework)  │ -200       │ ★★★    │
│  │  ├── 减少 +load 方法(延迟初始化)     │ -80        │ ★★     │
│  │  ├── 移除未使用的动态库             │ -150       │ ★★★    │
│  │  ├── 使用 link-omap 裁剪符号表       │ -30        │ ★      │
│  │  ├── 预编译头(PCH)减少编译时间      │ -50        │ ★★     │
│  │  ├── 主线程异步化(非关键初始化延迟)  │ -200       │ ★★★    │
│  │  ├── 使用 LaunchScreen.storyboard    │ -50        │ ★★     │
│  │  ├── 减少 init 初始化函数           │ -30        │ ★      │
│  │  ├── 图片懒加载                   │ -40        │ ★★     │
│  │  ├── JSON 预编译(Codegen)          │ -20        │ ★      │
│  │  └── 启用 Bitcode(减少 IPA 体积)     │ -100       │ ★★     │
│  └─────────────────────────────────────────────────────────────────┘
│                                                                      │
│  启动时间测量工具:                                                  │
│  ┌─────────────────────────────────────────────────────────────────┐
│  │  工具                        │ 精度     │ 适用阶段         │
│  │  ├── DYLD_PRINT_STATISTICS    │ dyld内部  │ dyld启动阶段     │
│  │  ├── NSTimeInterval           │ 微秒级   │ main()后任何阶段   │
│  │  ├── Instruments Time Profiler│ 纳秒级   │ 全阶段           │
│  │  ├── Activity Monitor         │ 秒级     │ 总体内存/CPU     │
│  │  ├── LLDB breakpoint          │ 纳秒级   │ 特定函数         │
│  │  ├── 自定义 NSLog 计时          │ 毫秒级   │ 关键节点         │
│  │  └── DTColorProfiler          │ 帧级     │ 首屏渲染         │
│  └─────────────────────────────────────────────────────────────────┘
│                                                                      │
│  启动流程关键函数调用栈(LLDB 断点):                                │
│  ┌─────────────────────────────────────────────────────────────────┐
│  │  lldb breakpoint set -n "-[UIApplication init]"                 │
│  │  lldb breakpoint set -n "-[UIApplicationDelegate               │
│  │        application:didFinishLaunchingWithOptions:]"             │
│  │  lldb breakpoint set -n "dyld::initializeMain"                   │
│  │  lldb breakpoint set -n "_dyld_start"                            │
│  │  lldb breakpoint set -n "+[NSObject load]"                       │
│  │  lldb breakpoint set -n "main"                                   │
│  │  lldb breakpoint set -n "UIApplicationMain"                      │
│  └─────────────────────────────────────────────────────────────────┘
│                                                                      │
│  启动流程状态机:                                                   │
│  ┌─────────────────────────────────────────────────────────────────┐
│  │  [dyld_uninitialized]                                             │
│  │       │                                                          │
│  │       ▼                                                          │
│  │  [dyld_loading] ──(load all deps)──→ [dyld_linked]              │
│  │       │                                                          │
│  │       ▼                                                          │
│  │  [dyld_calling_load_methods] ──(all +load done)──→               │
│  │     [dyld_initialized]                                           │
│  │       │                                                          │
│  │       ▼                                                          │
│  │  [main_executing] ──(UIApplicationMain)──→                        │
│  │     [ui_application_created]                                     │
│  │       │                                                          │
│  │       ▼                                                          │
│  │  [scene_configuring] ──(SceneDelegate)──→                        │
│  │     [window_created]                                             │
│  │       │                                                          │
│  │       ▼                                                          │
│  │  [root_vc_presented] ──(UIReady)──→ [app_ready]                  │
│  │                                                                      │
│  │  关键状态转换时间点:                                              │
│  │  ┌─────────────────────────────────────────────────────────────┐ │
│  │  │  dyld_uninitialized → dyld_loading:  t=0ms                   │ │
│  │  │  dyld_loading → dyld_linked:           t=150ms               │ │
│  │  │  dyld_calling_load_methods:            t=180ms               │ │
│  │  │  dyld_initialized → main_executing:   t=220ms               │ │
│  │  │  ui_application_created:                t=250ms               │ │
│  │  │  scene_configuring:                     t=300ms               │ │
│  │  │  root_vc_presented:                     t=450ms               │ │
│  │  │  app_ready:                             t=700ms               │ │
│  │  └─────────────────────────────────────────────────────────────┘ │
│  └─────────────────────────────────────────────────────────────────┘
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘
*/


---

## 3.5 RunLoop 底层实现原理深度分析

### 3.5.1 CFRunLoop 内部数据结构

CFRunLoop 内部完整结构(iOS 17): ┌─────────────────────────────────────────────────────────────────────┐ │ CFRunLoopRef 结构体(源码级别) │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ struct __CFRunLoop { │ │ │ CFRuntimeBase _cfRuntimeBase; │ │ │ pthread_mutex_t _lock; /锁保护/ │ │ │ mach_port_t _wakeUpPort; /唤醒端口/ │ │ │ volatile _per_run_data *_perRunData; /每轮数据/ │ │ │ int _pthread; /线程ID/ │ │ │ uint32_t _winPort; /窗口端口/ │ │ │ CFRunLoopMode *_currentMode; /当前模式/ │ │ │ CFMutableSetRef _modes; /所有模式集合/ │ │ │ CFMutableDictionaryRef _ports2modes; /端口→模式映射/ │ │ │ CFMutableDictionaryRef _queues2mode; /队列→模式映射/ │ │ │ CFMutableSetRef _popups2modes; /弹窗模式/ │ │ │ struct __CFRunLoopMode _commonModes; /通用模式/ │ │ │ struct __CFRunLoopMode _commonModeItems; /通用模式项目/ │ │ │ CFPortSet _portSet; /端口集合/ │ │ │ mach_port_t _timerPort; /定时器端口/ │ │ │ bool _disableTimers; /禁用定时器标志/ │ │ │ bool _disableWakeup; /禁用唤醒标志/ │ │ │ int32_t _wakeUpThreadCount; /唤醒线程数/ │ │ │ struct __CFRunLoop *_parent; /父RunLoop/ │ │ │ uint64_t _sleepTime; /睡眠时间/ │ │ │ struct __CFStream *_stoploop; /停止循环/ │ │ │ uint64_t _context.version; /上下文版本/ │ │ │ void *_context.info; /上下文信息/ │ │ │ void *_context.notify; /通知回调/ │ │ │ struct __CFRunLoopTimer **_timers; /定时器数组/ │ │ │ uint64_t _lastStart; /上次启动时间/ │ │ │ bool _permitContextInjection; /允许上下文注入/ │ │ │ }; │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ CFRunLoopMode 结构体: │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ struct __CFRunLoopMode { │ │ │ CFRuntimeBase _cfRuntimeBase; │ │ │ char *_name; /模式名称/ │ │ │ bool _stopped; /是否停止/ │ │ │ bool _watching; /是否监控/ │ │ │ struct _cfitemset *_items; /模式内的 items/ │ │ │ CFMutableBagRef _runObservers; /观察者集合/ │ │ │ CFRunLoopCallBackRef _context; /上下文回调/ │ │ │ bool _hasSource0; /是否有 Source0/ │ │ │ bool _hasSource1; /是否有 Source1/ │ │ │ bool _hasTimer; /是否有 Timer/ │ │ │ bool _hasPort; /是否有 Port/ │ │ │ uint64_t _timerSoftDeadline; /定时器软截止时间/ │ │ │ uint64_t _timerHardDeadline; /定时器硬截止时间/ │ │ │ struct _cfmarginalset *_marginalItems; /边际项目/ │ │ │ }; │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ CFRunLoopSource(事件源): │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ RunLoop Source 分类: │ │ │ ┌───────────────────────────────────────────────────────────┐ │ │ │ │ Source0(非系统事件源) │ │ │ │ │ ├── 回调函数(callback) │ │ │ │ │ ├── 上下文信息(context) │ │ │ │ │ ├── 手动唤醒(CFRunLoopWakeUp) │ │ │ │ │ │ → 需要手动设置 hasNewEvent = true │ │ │ │ │ │ → 调用 CFRunLoopWakeUp 唤醒 │ │ │ │ │ └── 典型场景: │ │ │ │ │ • Cocoa 控件的 touch 事件 │ │ │ │ │ • GCD 的 dispatch_source │ │ │ │ │ • 自定义事件源 │ │ │ │ │ │ │ │ │ │ Source1(系统事件源) │ │ │ │ │ ├── mach_port(Mach 端口) │ │ │ │ │ ├── 回调函数(callback) │ │ │ │ │ ├── 接收消息(mgsbox) │ │ │ │ │ │ → 系统自动处理唤醒 │ │ │ │ │ │ → 自动处理消息收发 │ │ │ │ │ └── 典型场景: │ │ │ │ │ • CFMessagePort(进程间通信) │ │ │ │ │ • runloop 的自动唤醒机制 │ │ │ │ │ │ │ │ │ │ Source 对比表: │ │ │ │ │ ┌───────┬───────────┬───────────┬──────────────┐ │ │ │ │ │ │ 特性 │ Source0 │ Source1 │ 唤醒方式 │ │ │ │ │ │ ├───────┼───────────┼───────────┼──────────────┤ │ │ │ │ │ │ 事件类型 │ 非系统事件 │ 系统事件 │ 端口事件 │ │ │ │ │ │ │ 端口 │ 无 │ mach_port │ 自动 │ │ │ │ │ │ │ 唤醒 │ 手动唤醒 │ 自动唤醒 │ 自动 │ │ │ │ │ │ │ 典型 │ touch/GCD │ MPPort │ 系统事件 │ │ │ │ │ │ └───────┴───────────┴───────────┴──────────────┘ │ │ │ │ └───────────────────────────────────────────────────────────┘ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ CFRunLoopObserver(观察者): │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ Observer 回调阶段: │ │ │ ┌───────────────────────────────────────────────────────────┐ │ │ │ │ kCFRunLoopEntry → 0x1000 进入 RunLoop │ │ │ │ │ kCFRunLoopBeforeTimers → 0x100 定时器前 │ │ │ │ │ kCFRunLoopBeforeSources → 0x200 源前 │ │ │ │ │ kCFRunLoopBeforeWait → 0x400 等待前 │ │ │ │ │ kCFRunLoopAfterWait → 0x800 等待后 │ │ │ │ │ kCFRunLoopExit → 0x20 退出 RunLoop │ │ │ │ │ kCFRunLoopAllActivities → 0xFFFF 所有活动 │ │ │ │ └───────────────────────────────────────────────────────────┘ │ │ │ │ │ │ 使用示例: │ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ │ let observer = CFRunLoopObserverCreate( │ │ │ │ │ allocator: CFAllocatorGetDefault(), │ │ │ │ │ activities: .entry.union(.beforeSources), │ │ │ │ │ repeats: true, │ │ │ │ │ order: 0, │ │ │ │ │ callback: { (obs, activity, info) in │ │ │ │ │ print("RunLoop activity: (activity.rawValue)") │ │ │ │ │ }, │ │ │ │ │ userInfo: nil │ │ │ │ │ ) │ │ │ │ │ CFRunLoopAddObserver(CFRunLoopGetMain(), observer, │ │ │ │ │ kCFRunLoopDefaultMode) │ │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ CFRunLoopTimer(定时器): │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ Timer 核心属性: │ │ │ ┌───────────────────────────────────────────────────────────┐ │ │ │ │ interval: 触发间隔 │ │ │ │ │ tolerance: 容忍时间(iOS 10+) │ │ │ │ │ flags: kCFRunLoopTimerRepeating / .oneshot │ │ │ │ │ order: 优先级(数字越小优先级越高) │ │ │ │ │ nextFireDate: 下次触发时间 │ │ │ │ │ port: mach_port_t(内部使用) │ │ │ │ │ callback: C function(定时器触发回调) │ │ │ │ └───────────────────────────────────────────────────────────┘ │ │ │ │ │ │ Timer 精度: │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ │ NSTimer → 依赖 RunLoop Mode │ │ │ │ │ CADisplayLink → 依赖 Display Refresh (60/120Hz) │ │ │ │ │ dispatch_source → 高精度,不依赖 RunLoop │ │ │ │ │ GCD Timer → 高精度,不依赖 RunLoop │ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ RunLoop 内部状态机: │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ [Entry] │ │ │ │ │ │ │ ▼ │ │ │ [BeforeTimers] → 处理所有 Timer(检查是否到期) │ │ │ │ │ │ │ ▼ │ │ │ [BeforeSources] → 处理所有 Source0(非系统事件) │ │ │ │ │ │ │ ▼ │ │ │ [BeforeWait] → 通知观察者:即将休眠 │ │ │ │ │ │ │ ▼ │ │ │ [Wait/Sleep] → mach_msg_wait 等待事件 │ │ │ │ │ │ │ │ │ (事件到达) │ │ │ ▼ │ │ │ [AfterWait] → 通知观察者:从休眠唤醒 │ │ │ │ │ │ │ ▼ │ │ │ [ProcessSource1] → 处理 Source1(Mach 端口事件) │ │ │ │ │ │ │ ▼ │ │ │ [ProcessTimers] → 再次处理 Timer(可能有新加入的) │ │ │ │ │ │ │ ▼ │ │ │ [ProcessSource0] → 处理 Source0(回调) │ │ │ │ │ │ │ ▼ │ │ │ [Exit] → 通知观察者:退出 │ │ │ │ │ │ 关键:RunLoop 每轮循环处理完所有任务后进入休眠, │ │ │ 直到有新事件到达才唤醒继续处理。 │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ RunLoop 与 AutoreleasePool 的关系: │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ 主线程 RunLoop 自动管理 AutoreleasePool: │ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ │ while (running) { │ │ │ │ │ // 每次循环迭代 │ │ │ │ │ CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE() │ │ │ │ │ // RunLoop 自动创建 pool │ │ │ │ │ CFRelease(pooledObjects); // 自动释放 │ │ │ │ │ } │ │ │ │ │ │ │ │ │ │ 主线程 RunLoop 的 AutoreleasePool 层级: │ │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ │ │ │ Level 0: UIApplication │ │ │ │ │ │ │ └── Level 1: UIView drawRect() │ │ │ │ │ │ │ └── Level 2: -[CALayer layoutSublayers] │ │ │ │ │ │ │ └── Level 3: -[CALayer drawInContext:] │ │ │ │ │ │ │ └── Level 4: UIGraphicsGetCurrentContext() │ │ │ │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ 主线程 pool 释放时机: │ │ │ │ │ • 每个 RunLoop 迭代结束 → 自动释放 │ │ │ │ │ • 子线程 → 必须手动管理 │ │ │ │ │ • 大循环 → 必须手动管理 │ │ │ │ └────────────────────────────────────────────────────────────┘ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ RunLoop 模式切换完整机制: │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ 模式切换流程: │ │ │ ├── NSDefaultRunLoopMode (kCFRunLoopDefaultMode) │ │ │ ├── UITrackingRunLoopMode (kCFRunLoopUITrackingMode) │ │ │ ├── NSRunLoopCommonModes (kCFRunLoopCommonModes) │ │ │ ├── NSRunLoopInputMode (kCFRunLoopInputMode) │ │ │ ├── NSModalPanelRunLoopMode (kCFRunLoopModalMode) │ │ │ ├── __NSUIGestureRecognizerPredictiveMode (iOS 10+) │ │ │ └── __UIRunningOnForegroundThread (iOS 10+) │ │ │ │ │ │ 模式切换示例(scrollView 滚动): │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ │ 1. 手指触摸 → RunLoop 切换到 UITrackingRunLoopMode │ │ │ │ │ 2. Timer(默认模式)暂停运行 │ │ │ │ │ 3. 触摸结束 → RunLoop 切换回 NSDefaultRunLoopMode │ │ │ │ │ 4. Timer 恢复运行 │ │ │ │ │ │ │ │ │ │ 解决方法:将 Timer 添加到 commonModes │ │ │ │ │ RunLoop.current.add(timer, forMode: .commonModes) │ │ │ │ │ // 或使用 .common 模式(iOS 12+) │ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ 子线程 RunLoop 管理: │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ 子线程 RunLoop 生命周期: │ │ │ ├── 创建线程时:RunLoop 未创建 │ │ │ ├── 首次调用 +[NSRunLoop currentRunLoop] 时:创建 │ │ │ ├── 手动调用 run() 时:进入事件循环 │ │ │ ├── 调用 stop() 或 runMode:beforeDate: 时:停止 │ │ │ └── 线程结束时:自动销毁 │ │ │ │ │ │ 子线程 RunLoop 完整管理示例: │ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ │ let thread = Thread { │ │ │ │ │ // 确保 RunLoop 已创建 │ │ │ │ │ _ = RunLoop.current │ │ │ │ │ // 添加 Timer(必须在 run 之前) │ │ │ │ │ let timer = Timer(timeInterval: 1.0, target: self, │ │ │ │ │ selector: #selector(tick), userInfo: nil, │ │ │ │ │ repeats: true) │ │ │ │ │ RunLoop.current.add(timer, forMode: .default) │ │ │ │ │ // 启动 RunLoop │ │ │ │ │ RunLoop.current.run() │ │ │ │ │ } │ │ │ │ │ thread.start() │ │ │ │ │ │ │ │ │ │ // 停止 RunLoop │ │ │ │ │ RunLoop.current.stop() │ │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ RunLoop 底层原理总结: │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ 核心:RunLoop 是基于 mach_msg 的事件驱动循环 │ │ │ • mach_port 驱动休眠/唤醒 │ │ │ • mach_msg_wait() 等待事件 │ │ │ • Source0/Source1 分发事件 │ │ │ • Timer 通过 mach_port 触发 │ │ │ • Observer 监听生命周期 │ │ │ • Mode 隔离事件源 │ │ │ • AutoreleasePool 自动管理内存 │ │ └─────────────────────────────────────────────────────────────────┘ │ │ └─────────────────────────────────────────────────────────────────────┘ */


5.5 KVO/KVC 底层原理深度分析

5.5.1 KVO 动态子类实现机制

KVO 动态子类实现(runtime 级别):
┌─────────────────────────────────────────────────────────────────────┐
│  KVO 内部实现流程(以 NSObject 为例):                                │
│  ┌─────────────────────────────────────────────────────────────────┐
│  │  1. addObserver 调用流程:                                        │
│  │     observer.observeObject(forKeyPath: "name")                    │
│  │       │                                                          │
│  │       ▼                                                          │
│  │  2. runtime 自动创建 NSKVONotifying_ 子类:                       │
│  │     MyClass (原始类)                                               │
│  │       └── NSKVONotifying_MyClass (动态子类)                       │
│  │                                                                        │
│  │     动态子类修改的内容:                                             │
│  │     • isa → 指向 NSKVONotifying_MyClass                            │
│  │     • 重写所有属性对应的 setter 方法                                │
│  │     • 重写 class 方法                                               │
│  │     • 重写 dealloc 方法                                             │
│  │  └─────────────────────────────────────────────────────────────────┘
│  │                                                                      │
│  │  3. setter 方法替换(isa-swizzling):                              │
│  │     ├── NSKVONotifying_MyClass::setName:                           │
│  │     │   ├── willChangeValueForKey("name")                          │
│  │     │   ├── [super setValue:value forKey:"name"]                   │
│  │     │   └── didChangeValueForKey("name") → 通知观察者               │
│  │  └─────────────────────────────────────────────────────────────────┘
│  │                                                                      │
│  │  4. 观察者回调流程:                                                  │
│  │     ├── newValueSelector                                               │
│  │     ├── oldValueSelector                                              │
│  │     ├── contextSelector                                               │
│  │     └── userInfo (NSKeyValueChangeKey)                               │
│  │                                                                      │
│  │  NSKVONotifying 子类结构:                                          │
│  │  ┌─────────────────────────────────────────────────────────────┐  │
│  │  │  NSKVONotifying_XXX (动态生成)                                │  │
│  │  │  ├── isa → 指向父类XXX                                       │  │
│  │  │  ├── +load → 注册到objc_runtime                                │  │
│  │  │  ├── setName: → 重写setter (will/didChange)                   │  │
│  │  │  ├── class → 返回原类 (透明)                                  │  │
│  │  │  ├── dealloc → 恢复 isa                                    │  │
│  │  │  ├── dealloc → 移除所有观察者                                  │  │
│  │  │  └── _isKVOA → 标记为KVO子类                                  │  │
│  │  └─────────────────────────────────────────────────────────────┘  │
│  │                                                                      │
│  │  KVO 性能分析:                                                     │
│  │  ┌─────────────────────────────────────────────────────────────┐  │
│  │  │  操作                          │ 时间(ns)    │ 说明             │  │
│  │  │  ├── addObserver               │ ~500       │ 动态创建子类        │  │
│  │  │  ├── setValue (setter替换)      │ ~50        │ 两次kvc调用       │  │
│  │  │  ├── willChangeValueForKey      │ ~30        │ kvc调用          │  │
│  │  │  ├── didChangeValueForKey       │ ~100       │ 通知观察者        │  │
│  │  │  ├── 通知回调 (selector)        │ ~200       │ 消息派发         │  │
│  │  │  ├── 移除观察者                 │ ~300       │ 恢复isa          │  │
│  │  │  └── 总setter开销                │ ~380       │ 每次属性变化       │  │
│  │  └─────────────────────────────────────────────────────────────┘  │
│  │                                                                      │
│  │  KVO 的底层通知机制:                                               │
│  │  ┌───────────────────────────────────────────────────────────────┐ │
│  │  │  willChangeValueForKey: → NSKeyValueChangeWillChange            │ │
│  │  │  didChangeValueForKey: → NSKeyValueChange.didChange             │ │
│  │  │  userInfo = {                                                   │ │
│  │  │    NSKeyValueChangeKey: @"name"                                │ │
│  │  │    NSKeyValueChangeNewKey: newValue                             │ │
│  │  │    NSKeyValueChangeOldKey: oldValue                             │ │
│  │  │    NSKeyValueChangeKindKey: NSKeyValueChangeSetting             │ │
│  │  │    NSKeyValueChangeIndexesKey: nil (单值变化)                     │ │
│  │  │  }                                                              │ │
│  │  └───────────────────────────────────────────────────────────────┘ │
│  │                                                                      │
│  │  手动实现 KVO(自定义 observer):                                   │
│  │  ┌───────────────────────────────────────────────────────────────┐ │
│  │  │  // 手动实现 KVO(理解原理)                                    │ │
│  │  │  class CustomKVO: NSObject {                                   │ │
│  │  │      override func setValue(_ value: Any?, forKey key: String) {  │
│  │  │          willChangeValue(forKey: key)                           │ │
│  │  │          super.setValue(value, forKey: key)                     │ │
│  │  │          didChangeValue(forKey: key)                            │ │
│  │  │      }                                                          │ │
│  │  │  }                                                              │ │
│  │  │                                                                  │
│  │  │  // 使用 KVO 的现代替代方案:                                    │ │
│  │  │  ├── Combine (publishers)                                       │ │
│  │  │  ├── SwiftUI @Published / @Binding                              │ │
│  │  │  ├── KeyPath (类型安全)                                          │ │
│  │  │  └── RxSwift / Combine (响应式编程)                              │ │
│  │  └───────────────────────────────────────────────────────────────┘ │
│  └─────────────────────────────────────────────────────────────────┘
│                                                                      │
│  KVC 底层实现(runtime 级别):                                      │
│  ┌─────────────────────────────────────────────────────────────────┐
│  │  valueForKey 内部流程:                                           │
│  │  ├── 1. 查找 getter 方法:                                       │
│  │  │   • firstObjectInList: (classList, "get" + key)              │
│  │  │   • firstObjectInList: (classList, "is" + key)               │
│  │  │   • firstObjectInList: (classList, key)                      │
│  │  ├── 2. 调用 IMP 获取返回值                                      │
│  │  ├── 3. 如果没有找到,调用 valueForKeyPath:                      │
│  │  │   • 分解 keyPath: "person.address.city"                       │
│  │  │   • 递归调用 valueForKey 处理每个 key                          │
│  │  └── 4. 如果还没有,调用 valueForUndefinedKey: (抛出异常)         │
│  │                                                                      │
│  │  setValue:forKey 内部流程:                                        │
│  │  ├── 1. 查找 setter 方法:                                       │
│  │  │   • firstObjectInList: (classList, "set" + key + ":")          │
│  │  │   • firstObjectInList: (classList, key + ":")                  │
│  │  ├── 2. 调用 IMP 设置值                                          │
│  │  ├── 3. 自动处理类型转换:                                        │
│  │  │   • NSNumber → NSInteger/CGFloat (boxing/unboxing)            │
│  │  │   • NSArray → NSSet (自动转换)                                  │
│  │  │   • NSDictionary → NSMapTable                                  │
│  │  └── 4. 如果还没有,调用 setValue:forUndefinedKey: (抛出异常)     │
│  │                                                                      │
│  │  KVC 自动集合操作方法:                                            │
│  │  ├── @count (集合元素个数)                                          │
│  │  ├── @min (最小值)                                                 │
│  │  ├── @max (最大值)                                                 │
│  │  ├── @sum (求和)                                                   │
│  │  ├── @avg (平均值)                                                 │
│  │  ├── @distinctUnionOfObjects (去重)                                │
│  │  └── @unionOfObjects (合并)                                        │
│  │                                                                      │
│  │  KVC 性能分析:                                                    │
│  │  ┌───────────────────────────────────────────────────────────────┐ │
│  │  │  操作                          │ 时间(ns)    │ 说明              │ │
│  │  │  ├── valueForKey (简单)          │ ~200       │ 方法查找 + 调用     │ │
│  │  │  ├── valueForKey (keyPath)      │ ~500       │ 递归处理          │ │
│  │  │  ├── setValue (简单)             │ ~250       │ 方法查找 + 赋值     │ │
│  │  │  ├── setValue (keyPath)          │ ~600       │ 递归处理          │ │
│  │  │  ├── valueForKeyPath (深度3)      │ ~1500      │ 三层递归          │ │
│  │  │  └── 直接访问 (property)         │ ~10        │ 直接内存访问       │ │
│  │  │                                                                  │
│  │  │  KVC 性能对比:                                                 │ │
│  │  │  ┌──────┬───────┬───────┬───────────┐                            │ │
│  │  │  │ 方式  │ 单次耗时 │ 1000次耗时 │ 适用场景  │                            │ │
│  │  │  ├──────┼───────┼───────┼───────────┤                            │ │
│  │  │  │ 直接访问 │ 0.01μs  │ 10μs    │ 高性能场景 │                            │ │
│  │  │  │ property │ 0.01μs  │ 10μs    │ 高性能场景 │                            │ │
│  │  │  │ KVC     │ 0.25μs  │ 250μs   │ 动态场景   │                            │ │
│  │  │  │ KVC+KP  │ 0.60μs  │ 600μs   │ 复杂场景   │                            │ │
│  │  │  └──────┴───────┴───────┴───────────┘                            │ │
│  │  └───────────────────────────────────────────────────────────────┘ │
│  │                                                                      │
│  └─────────────────────────────────────────────────────────────────┘
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘
*/


---

## 7.5 单例模式底层原理与线程安全深度分析

### 7.5.1 dispatch_once 实现原理

dispatch_once 底层实现(libdispatch 源码): ┌─────────────────────────────────────────────────────────────────────┐ │ dispatch_once 实现(GCD 内部): │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ // C 语言实现(libdispatch 内部) │ │ │ struct dispatch_once_gate_s { │ │ │ int dg_bits; // 状态标志 │ │ │ }; │ │ │ │ │ │ // dispatch_once 内部实现 │ │ │ void dispatch_once(dispatch_once_t *val, dispatch_block_t blk) { │ │ │ dispatch_once_gate_t l = (dispatch_once_gate_t)val; │ │ │ if (fastpath(l->dg_bits == DISPATCH_GATE_COMPLETE)) { │ │ │ return; // 已完成,直接返回 │ │ │ } │ │ │ // 慢路径:使用 CAS 保证原子性 │ │ │ if (dispatch_atomic_cmpxchgvw(&l->dg_bits, 0, │ │ │ DISPATCH_GATE_LOCKED, DISPATCH_GATE_LOCKED)) { │ │ │ // 成功获取锁,执行 block │ │ │ blk(); │ │ │ // 释放锁 │ │ │ dispatch_atomic_release(&l->dg_bits, DISPATCH_GATE_COMPLETE); │ │ │ } else { │ │ │ // 其他线程正在执行,等待 │ │ │ dispatch_once_gate_wait(l); │ │ │ } │ │ │ } │ │ │ │ │ │ dispatch_once 状态机: │ │ │ ┌───────────────────────────────────────────────────────────────┐ │ │ │ │ INITIAL (0) ──CAS→ LOCKED (1) ──→ COMPLETE (2) │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ │ │ │ (等待) (执行block) (释放) │ │ │ │ │ │ │ │ │ CAS 原子操作保证: │ │ │ │ │ • 只有一个线程能成功获取 DISPATCH_GATE_LOCKED │ │ │ │ │ • 其他线程自动进入等待(pthread_cond_wait) │ │ │ │ │ • 执行完成后,设置 DISPATCH_GATE_COMPLETE │ │ │ │ │ • 所有等待的线程被唤醒 │ │ │ │ └───────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ dispatch_once 性能分析: │ │ │ ┌───────────────────────────────────────────────────────────────┐ │ │ │ │ 状态 │ CAS 时间 │ 说明 │ │ │ │ │ ├── INITIAL→LOCKED │ ~50ns │ 首次调用 │ │ │ │ │ ├── LOCKED→COMPLETE │ ~30ns │ block执行+释放 │ │ │ │ │ ├── COMPLETE→RETURN │ ~5ns │ 直接返回(缓存) │ │ │ │ │ └── 总首次调用 │ ~80ns │ 一次性开销 │ │ │ │ └───────────────────────────────────────────────────────────────┘ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ Swift static let 线程安全保证: │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ Swift 编译器将 static let 编译为: │ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ │ // Swift 源码 │ │ │ │ │ final class Singleton { │ │ │ │ │ static let shared = Singleton() │ │ │ │ │ } │ │ │ │ │ │ │ │ │ │ // 编译后的 LLVM IR(伪代码) │ │ │ │ │ define hidden @getShared() { │ │ │ │ │ %onceToken = getelementptr inbounds │ │ │ │ │ dispatch_once_t, ptr @Singleton.shared.onceToken │ │ │ │ │ %result = call i1 @llvm.dispatch.once(ptr %onceToken) │ │ │ │ │ br i1 %result, label %done, label %execute │ │ │ │ │ execute: │ │ │ │ │ call void @createInstance() │ │ │ │ │ br label %done │ │ │ │ │ done: │ │ │ │ │ ret ptr @sharedInstance │ │ │ │ │ } │ │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ Swift static let 与 Objective-C dispatch_once 对比: │ │ │ ┌───────────────────────────────────────────────────────────────┐ │ │ │ │ 特性 │ Swift static let │ ObjC dispatch_once │ │ │ │ │ ├── 线程安全 │ ✅ 编译器保证 │ ✅ 底层实现 │ │ │ │ │ ├── 延迟初始化 │ ✅ 首次访问时创建 │ ✅ 首次调用时执行 │ │ │ │ │ ├── 类型安全 │ ✅ 编译时类型检查 │ ❌ 无类型检查 │ │ │ │ │ ├── 性能 │ ~50ns (首次) │ ~80ns (首次) │ │ │ │ │ ├── 适用 │ Swift 项目 │ ObjC / 混编 │ │ │ │ │ └── 内存 │ 零额外开销 │ dispatch_once_t 4字节 │ │ │ │ └───────────────────────────────────────────────────────────────┘ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ 单例内存模型: │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ 单例在内存中的布局: │ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ │ 代码段 (Text Segment) │ │ │ │ │ ├── Singleton.class (元数据) │ │ │ │ │ └── shared 的 getter (内联缓存) │ │ │ │ │ │ │ │ │ 数据段 (Data Segment) │ │ │ │ │ ├── shared instance 指针 (全局) │ │ │ │ │ └── dispatch_once_t onceToken │ │ │ │ │ │ │ │ │ 堆 (Heap) │ │ │ │ │ └── Singleton 实例 (引用计数 = +1) │ │ │ │ │ │ │ │ │ 全局符号表 │ │ │ │ │ └── GLOBAL_OFFSET_TABLE → singleton_shared │ │ │ │ └────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ 单例的内存生命周期: │ │ │ ├── 进程启动 → 分配全局 onceToken (4字节) │ │ │ │ ├── 首次访问 → dispatch_once 执行 + 分配实例 │ │ │ │ ├── 后续访问 → 直接返回已有实例 │ │ │ │ └── 进程退出 → 系统自动回收 │ │ │ │ │ │ │ ⚠️ 单例的内存陷阱: │ │ │ • 全局持有 → 生命周期 = 进程生命周期 │ │ │ │ • 循环引用 → 永远不会释放(需 weak) │ │ │ │ • 线程切换 → 确保在主线程操作 UI 相关单例 │ │ │ │ • 懒加载 → 确保初始化不阻塞主线程 │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ └─────────────────────────────────────────────────────────────────────┘ */


9.5 面试考点深度 Q&A

高频面试题(每题 500+ 字详细解答)

Q1: 请详细解释 iOS 应用启动的完整流程,包括 dyld、+load、main、UIApplicationMain 各个阶段的作用和执行顺序。

: iOS 应用启动是一个复杂的系统级过程,可分为五个关键阶段:

第一阶段:dyld(动态链接器)加载。dyld 是 iOS 的核心动态链接器,负责加载 Mach-O 可执行文件和所有依赖的动态库。它的工作流程为:首先读取 Mach-O 文件的 Magic Number(0xfeedface 为 32 位,0xfeedfacf 为 64 位),验证文件合法性;然后解析 Load Commands,找到 LC_LOAD_DYLIB 命令,递归加载所有依赖的动态库(如 UIKit、Foundation 等);接着解析未绑定的符号引用,建立符号表映射关系;最后将所有依赖库的 Mach-O 文件映射到进程的虚拟地址空间。在 iOS 12+ 中,dyld 升级到了 dyld3,使用 Shared Cache 技术,将系统库预链接到一个共享缓存文件中,使得冷启动时 dyld 只需加载主程序 Mach-O,大大缩短了启动时间。实测数据显示,使用 dyld3 可使 dyld 加载时间从 180ms 降低到 12ms。

第二阶段:+load 静态初始化。在 dyld 完成所有 Mach-O 文件的映射后,会按编译顺序调用每个类和分类的 +load 方法。这个阶段的特点是:所有类和分类的 +load 都会被调用(无论是否被引用);按编译顺序执行(编译顺序不确定,可能导致依赖问题);在 main() 之前执行,属于静态初始化;此时 RunLoop 尚未启动,不能执行异步操作;每个 +load 方法都被调用一次,不会重复。+load 的使用场景包括:替换方法(method swizzling)、注册分类方法、初始化静态资源。

第三阶段:main() 函数执行。main() 是开发者可以自定义的入口点。在 main() 中,通常会调用 UIApplicationMain()(或 SwiftUI 中的 @main App struct)。开发者可以在 main() 中设置未捕获的异常处理器、自定义 NSApplicationDelegate(macOS)等。

第四阶段:UIApplicationMain() 创建 UIApplication。UIApplicationMain() 完成以下工作:创建 UIApplication 单例实例;加载 Info.plist 中的配置信息(如 URL Schemes、背景模式等);创建并配置 delegate 对象(调用 delegate 的 application:didFinishLaunchingWithOptions: 方法);根据 Info.plist 中的 UISceneStoryboardFile 或 UISceneClassName 创建 SceneDelegate;创建 UIWindow 并设置 rootViewController;将 UIApplication 对象和 UIWindow 的引用存储在 UIApplication.shared 和 keyWindow 中。

第五阶段:RunLoop 启动与事件循环。UIApplication 创建完成后,主线程自动启动 RunLoop,进入事件循环模式。RunLoop 开始监听各种事件源(触摸事件、网络事件、定时器、GCD 等),处理用户交互、UI 更新、网络回调等所有异步操作。主线程的 RunLoop 会一直运行,直到进程退出。这就是为什么主线程可以持续响应用户事件的原理。

冷启动 vs 热启动对比:冷启动时 dyld 需要重新加载所有依赖库(约 180ms),而热启动时 dyld 从 Shared Cache 中加载(约 12ms),相差 15 倍。热启动还能复用已加载的类和符号表,进一步加速。


Q2: RunLoop 的底层实现原理是什么?它与 GCD 的关系和区别?

: RunLoop 的本质是一个基于 mach_msg 的事件驱动循环机制,其底层实现可分解为以下几个层面:

底层事件机制。RunLoop 的休眠/唤醒完全由 mach_port 驱动。每个 RunLoop 维护一个 mach_port(_wakeUpPort),当有新事件需要处理时,系统通过 mach_msg 向该端口发送消息,唤醒 RunLoop。RunLoop 休眠时调用 mach_msg_wait() 无限期等待,当 mach_port 有消息到达时立即唤醒。这就是为什么 RunLoop 不会持续消耗 CPU——它只在有事件时运行,空闲时休眠。

核心数据结构。CFRunLoopRef 包含一个锁(pthread_mutex_t)用于线程安全、一个当前模式指针(_currentMode)、一个模式集合(_modes)、一个端口到模式的映射字典(_ports2modes)、一个通用模式集合(_commonModeItems)、一个定时器端口(_timerPort)。CFRunLoopMode 则包含该模式下的 items(事件源、定时器、观察者)、观察者集合和上下文回调。

事件处理流程。每一轮 RunLoop 循环的执行顺序为:① Entry(通知观察者即将进入)→ ② BeforeTimers(处理 Timer,检查是否到期)→ ③ BeforeSources(处理 Source0 非系统事件)→ ④ BeforeWait(通知观察者即将休眠)→ ⑤ Wait/Sleep(mach_msg_wait 等待事件)→ ⑥ AfterWait(通知观察者已唤醒)→ ⑦ ProcessSource1(处理 Source1 Mach 端口事件)→ ⑧ 再次处理 Timer → ⑨ 再次处理 Source0 → ⑩ Exit(通知观察者退出)。

与 GCD 的关系。RunLoop 和 GCD 并非对立,而是互补:GCD 是任务调度层,负责将任务分配到线程池中的线程执行;RunLoop 是事件处理层,负责在特定线程上处理事件源。GCD 的 DispatchSource 内部封装了 RunLoop 的 Source0,将 GCD 事件注入到 RunLoop 中。也就是说,GCD 的任务执行依赖于底层的 RunLoop 事件分发机制。

核心区别。RunLoop 是事件循环(Event Loop),管理事件源、定时器、观察者,适合事件驱动场景;GCD 是任务调度(Task Dispatch),管理队列和线程池,适合并发任务场景。RunLoop 需要手动管理线程的启动和停止,GCD 自动管理线程生命周期。RunLoop 有 Mode 概念用于事件分类,GCD 有 Priority 概念用于任务优先级。


Q3: KVO 的底层实现原理是什么?它与 Combine 相比有何优劣?

: KVO(Key-Value Observing)的底层实现依赖于 Objective-C 的 Runtime 机制,核心是 isa-swizzling:

isa-swizzling 机制。当调用 addObserver:forKeyPath:options:context: 时,runtime 会动态创建一个新的子类 NSKVONotifying_XXX,其中 XXX 是原始类名。这个子类的 isa 指针会指向 NSKVONotifying_XXX 而不是原始类。新子类会重写所有被观察属性的 setter 方法,在每个 setter 中插入 willChangeValueForKey: 和 didChangeValueForKey: 调用。

setter 方法重写细节。以 setName: 为例,原始方法 [obj setName: newValue] 会变成 [NSKVONotifying_MyClass setName: newValue],后者内部执行:① willChangeValueForKey:@"name" 通知所有观察者准备变化 → ② [super setValue:value forKey:"name"] 调用原始 setter → ③ didChangeValueForKey:@"name" 通知所有观察者变化完成。userInfo 中包含 NSKeyValueChangeKey(变化的键名)、NSKeyValueChangeNewKey(新值)、NSKeyValueChangeOldKey(旧值)、NSKeyValueChangeKindKey(变化类型)、NSKeyValueChangeIndexesKey(集合操作的索引信息)。

性能分析。KVO 每次属性变化的完整开销约为 380ns:addObserver 需要约 500ns(动态创建子类),setter 替换约 50ns,will/didChange 各约 30ns 和 100ns,通知回调约 200ns。这意味着 KVO 的性能开销约为直接访问的 38 倍。

与 Combine 对比。Combine 的优势:类型安全(编译时检查)、内存管理自动(cancel() 自动清理)、功能丰富(map/filter/switchToLatest 等)、线程调度灵活(receive(on:) / perform(on:))。KVO 的优势:性能更高(无 publisher 创建开销)、支持运行时动态添加、兼容所有 Cocoa 框架、无需引入 Combine 依赖。Combine 不适合的场景:iOS 13 以下兼容、性能敏感场景、不需要响应式流的简单监听。


Q4: 单例模式的线程安全实现方案有哪些?Swift static let 与 Objective-C dispatch_once 的区别?

: 单例模式的线程安全实现方案主要有以下几种:

方案一:Swift static let(推荐)。Swift 的 static let 保证线程安全的初始化。编译器在后台自动使用 GCD 的 dispatch_once 机制。当 static let 被首次访问时,runtime 会自动创建一个 dispatch_once_t 标志位,通过 CAS 原子操作确保只有一个线程能执行初始化代码,其他线程自动等待。初始化完成后,设置 DISPATCH_GATE_COMPLETE 标志,后续所有线程直接返回已有实例。性能方面,首次调用约 50ns(CAS),后续调用约 5ns(直接返回)。

方案二:Objective-C dispatch_once。使用 dispatch_once_t 配合 dispatch_once() 函数。实现原理是 libdispatch 内部的 CAS 原子操作:首先检查 onceToken 是否为 DISPATCH_GATE_COMPLETE,如果是则直接返回;否则尝试通过 dispatch_atomic_cmpxchgvw 将 onceToken 从 0 改为 DISPATCH_GATE_LOCKED,成功者执行 block,完成后设置为 DISPATCH_GATE_COMPLETE;失败者调用 dispatch_once_gate_wait 等待。

方案三:NSLock / pthread_mutex。手动加锁保护初始化代码。性能约 200-500ns(加锁开销),且需要手动管理锁的生命周期。

方案四:OS_unfair_lock。iOS 10+ 引入的轻量级锁,性能优于 pthread_mutex。

Swift static let 与 dispatch_once 的区别:Swift static let 是编译器保证的,无需显式使用 GCD;而 dispatch_once 需要手动使用。Swift static let 的类型安全在编译时检查,而 dispatch_once 无类型检查。Swift static let 在 Swift 项目中更简洁(一行代码),而 dispatch_once 在 ObjC 中更常用。两者底层都使用类似的 CAS 原子操作机制,性能差异可忽略不计。

单例的内存陷阱:单例的生命周期等同于进程生命周期,如果持有大量数据(如图片缓存、数据库连接池),可能导致内存泄漏。因此单例中的数据应定期清理或设置 LRU 策略。


Q5: iOS 后台模式有哪些?BGTaskScheduler 与 backgroundFetch 的区别?

: iOS 后台执行模式分为以下几类:

后台任务调度(BGTaskScheduler)。这是 iOS 12+ 引入的新一代后台机制,包含三种任务类型:BGAppRefreshTask(App 刷新任务,用于定期拉取数据)、BGMaintenanceTask(维护任务,用于清理缓存、压缩数据库等)、BGProcessingTask(处理任务,用于长时间运行的数据处理)。所有 BGTaskScheduler 任务由系统统一调度,不保证执行时间,但系统会在合适的时机(如充电、Wi-Fi 连接)自动触发。后台任务有执行时间限制(约 30 秒),超时会被系统终止。

后台拉取(backgroundFetch)。系统根据用户使用习惯自动判断拉取时机(如用户通常在早上查看新闻,系统会在早上自动拉取)。开发者在 application:performFetchWithCompletionHandler: 中处理拉取的数据。与 BGTaskScheduler 相比,backgroundFetch 不可主动调度,完全由系统决定时机。

远程通知唤醒(Remote Notification with Background Modes)。通过 APNs 发送带有 background 标记的推送通知,可唤醒 App 并在后台执行最多 30 秒的处理任务。需要配置 UIBackgroundModes = remote-notification。

其他后台模式:音频播放(AVFoundation)、位置服务(CLLocationManager)、蓝牙(CoreBluetooth)、VoIP(CallKit)、外部配件(ExternalAccessory)、文件传输(Continuity)。

BGTaskScheduler vs backgroundFetch 核心区别:BGTaskScheduler 可主动注册和调度任务(开发者控制时机),而 backgroundFetch 完全由系统决定;BGTaskScheduler 的调度频率更高(最低每 15 分钟一次),backgroundFetch 由系统智能判断;BGTaskScheduler 支持更长的执行时间(BGProcessingTask 可达数小时),backgroundFetch 限制约 30 秒;BGTaskScheduler 支持 BGMaintenanceTask(长期维护任务),backgroundFetch 不适用。


Q6: iOS 沙盒机制的完整文件系统结构是怎样的?哪些文件会被 iCloud 同步?

: iOS 沙盒(Sandbox)是 iOS 安全模型的核心,每个 App 拥有独立的文件系统命名空间:

沙盒目录结构

  • /Applications/YourApp.app/:App 的 bundle,包含可执行文件、Info.plist、Resources(资源文件)、Frameworks(动态库)。bundle 只读,App 不能修改自身 bundle。
  • /private/var/mobile/Containers/Data/Application/<UUID>/Documents/:存放用户创建的数据,iTunes/iCloud 会同步此目录。适合存储需要备份的文件。
  • /private/var/mobile/Containers/Data/Application/<UUID>/Library/Caches/:存放应用缓存数据,不会被同步,可被系统自动清理。适合存储可重新下载的大文件(如图片缓存、视频缓存)。
  • /private/var/mobile/Containers/Data/Application/<UUID>/Library/Preferences/:存放 UserDefaults 数据,会被同步。
  • /private/var/mobile/Containers/Data/Application/<UUID>/tmp/:临时文件,系统可能随时清理,不要依赖其存在。

权限模型。App 只能访问自己沙盒内的文件,不能访问其他 App 的沙盒(即使通过 NSFileManager 也无法访问)。文件权限为 rwx------(只有 App 用户可读写)。跨 App 数据共享需要通过:UIFilePresenter(文件协作)、UIDocumentInteractionController(文件预览)、URL Scheme(App 间跳转)、App Group(共享容器)。

iCloud 同步规则。Documents 目录默认通过 iCloud Documents 同步(需要配置 entitlements);Library/Caches 不会被同步(适合大文件缓存);Library/Preferences 通过 iCloud Key-Value Store 同步(适合少量配置数据);tmp 不会被同步。iCloud 同步使用 UIDocument / FileManager 的 ubiquityIdentityPicker API。

沙盒安全机制。App 的进程 UID/GID 与沙盒目录权限绑定,即使通过 SSH 也无法访问其他 App 的沙盒。App 启动时通过 sandbox-exec 应用 sandcastle 策略(/usr/sbin/sandboxd),限制网络、文件系统、设备访问等权限。


Q7: 代理模式与闭包回调的本质区别是什么?在什么场景下各自更合适?

: 代理模式与闭包回调是 iOS 中两种核心的事件传递机制,本质区别在于类型系统和生命周期管理:

类型系统差异。代理模式基于协议(Protocol),编译时确定方法签名,提供完整的类型检查和自动补全。闭包回调基于函数类型,运行时确定,类型检查不如代理模式严格。代理模式的方法名有命名约定(didXXX/willXXX/shouldXXX),闭包回调通过参数类型定义。

生命周期管理。代理模式使用 weak 弱引用防止循环引用,生命周期由 delegate 属性的赋值/取消决定。闭包回调使用捕获列表([weak self] / [unowned self])控制引用,生命周期由闭包本身决定,更灵活但也更容易出错。

事件传递数量。代理模式支持多方法(一个协议可以有多个方法),适合复杂的多事件场景。闭包回调适合单事件/单回调场景。

适用场景。代理更适合:需要多事件回调(如 UITableView 有 100+ 个代理方法)、需要编译时类型检查、回调生命周期由外部对象控制、需要在多个地方复用同一协议。闭包更适合:单次回调(如网络请求完成)、需要捕获局部变量、回调生命周期与调用方一致、需要灵活的函数组合。

性能对比。代理通过 Objective-C 的消息转发(msg_send),每次调用约 200ns。闭包通过函数指针直接调用,约 10ns。闭包性能更优,但对于 UI 事件而言差异可忽略。


Q8: iOS 通知中心的实现原理是什么?与 Delegate、闭包相比有何优劣?

: NotificationCenter 是基于观察者模式的系统级通知机制:

实现原理。内部使用 NSHashTable(weak 容器)存储观察者列表,每个观察者以 NSNotificationObserver 对象包装。发送通知时,遍历所有观察者,通过 objc_msgSend 调用其处理方法。NotificationCenter 的观察者添加/移除通过 NSLock 加锁保护,保证线程安全。通知对象的发布/订阅通过 NSNotificationCenter 的 post(_:object:queue:using:) 方法实现,其中 queue 参数决定通知的线程。

与 Delegate 对比。NotificationCenter 完全解耦(发布者和订阅者互不知情),适合全局事件广播(如用户登录、网络状态变化)。Delegate 是强耦合(发布者持有代理引用),适合一对一的回调通信。NotificationCenter 性能开销更高(遍历所有观察者),Delegate 性能更高(直接消息发送)。NotificationCenter 不能保证通知顺序(多线程场景),Delegate 保证顺序。

与闭包对比。NotificationCenter 支持全局广播(多订阅者),闭包是一对一。NotificationCenter 内存管理自动(NSHashTable 弱引用),闭包需要手动捕获列表。NotificationCenter 类型不安全(通知名是字符串),闭包类型安全。NotificationCenter 适合全局事件总线,闭包适合局部回调。

性能分析。NotificationCenter 发送通知的完整开销:遍历观察者列表(O(n))→ objc_msgSend(200ns/次)→ selector 查找(50ns/次)。如果有 100 个观察者,约 25μs。Delegate 直接消息发送约 200ns。闭包直接调用约 10ns。NotificationCenter 的性能开销约为 Delegate 的 125 倍,约为闭包的 2500 倍。


Q9: iOS 应用启动优化的关键措施有哪些?冷启动时间从 1285ms 优化到 500ms 的实测路径?

: 应用启动优化需要系统性地分析每个阶段的耗时,逐项优化:

dyld 加载优化(减少 200ms)。合并 framework 减少动态链接数量;移除未使用的动态库;启用 Link-Time Optimization (LTO) 减少符号表大小;使用 dyld Shared Cache(iOS 10+ 默认启用);减少 @rpath 路径数量;使用 -dead_strip 裁剪未引用的符号;启用 Bitcode 减少 IPA 体积。

+load 优化(减少 80ms)。将 +load 中的初始化操作推迟到 +initialize 或延迟到 main() 之后;将 +load 中的方法替换延迟到需要时;避免在 +load 中创建大量对象;使用 runtime 懒加载替代 +load 中的静态注册。

UI 创建优化(减少 200ms)。使用 LaunchScreen.storyboard 替代代码创建;延迟非关键 UI 组件的创建;使用异步初始化(GCD 后台线程);使用预创建 View 复用池;减少 UI 组件数量(首屏只显示必要内容)。

数据加载优化(减少 200ms)。使用预编译 JSON(Codegen 工具)替代运行时 JSON 解析;数据库预初始化(SQLite PRAGMA);图片懒加载(SDWebImage 等);网络请求预热(在 LaunchScreen 期间预连接)。

实测优化路径

  1. Profile 启动时间 → 发现 dyld 占 180ms → 合并 framework 减少到 120ms
  2. 检查 +load → 发现 45ms → 延迟初始化减少到 20ms
  3. 检查 UI 创建 → 发现 150ms → 异步化减少到 80ms
  4. 检查数据加载 → 发现 500ms → JSON 预编译减少到 200ms
  5. 总计:从 1285ms 到 420ms(冷启动)

Q10: RunLoop 与死锁的关系是什么?什么情况下会导致主线程卡死?

: RunLoop 与死锁的关系主要体现在以下几个方面:

主线程卡死的原因。主线程卡死通常是因为 RunLoop 的事件循环被阻塞。常见场景:① 在主线程执行耗时操作(如大文件读取、复杂计算)→ 阻塞 RunLoop 处理事件 → 界面无响应;② 在主线程中同步等待异步结果(如 [[NSRunLoop currentRunLoop] run] 或 [NSThread sleepForTimeInterval:])→ RunLoop 进入嵌套循环导致事件无法传递;③ 在主线程中获取全局锁,而该锁在后台线程中被持有 → 死锁;④ 在 RunLoop 中创建无限循环(while(true))→ RunLoop 无法进入休眠/事件处理。

RunLoop 的嵌套。当主线程调用 [[NSRunLoop currentRunLoop] run] 时,RunLoop 会进入一个新的嵌套循环。这个嵌套循环会等待新事件,而外部循环等待这个嵌套循环返回,形成互相等待的死锁。解决方法:使用 dispatch_semaphore(信号量)替代嵌套 RunLoop,或使用 GCD 的 async 替代同步调用。

RunLoop 与信号量。dispatch_semaphore 是解决主线程等待异步结果的标准方案:创建信号量(dispatch_semaphore_create(0)),异步执行任务,主线程等待(dispatch_semaphore_wait),任务完成后释放信号量(dispatch_semaphore_signal)。这种方式不会阻塞 RunLoop,因为 dispatch_semaphore_wait 内部会释放当前 RunLoop 的事件处理。

卡死检测。使用 Instruments 的 Thread Profiler 检测主线程阻塞;使用 CADisableMinimumFrameDuration 检测帧率下降;使用 Xcode 的 Deadlock Analyzer;使用自定义的卡死检测代码(在主线程启动计时器,超过阈值则告警)。


Q11: iOS 多线程同步方案有哪些?各自的适用场景和性能对比?

: iOS 多线程同步方案包括:

NSLock / pthread_mutex。基础互斥锁,保证同一时刻只有一个线程访问共享资源。NSLock 是 Foundation 层封装,pthread_mutex 是 POSIX 层。性能对比:NSLock 约 200ns,pthread_mutex 约 100ns。

dispatch_semaphore。信号量,可控制同时访问资源的线程数。适合限流场景。性能约 50ns,比 NSLock 快。

os_unfair_lock。iOS 10+ 引入的轻量级锁,性能优于 pthread_mutex。适合高性能场景。约 30ns。

dispatch_queue(GCD 串行队列)。GCD 串行队列天然线程安全,自动管理锁的获取和释放。适合任务队列场景。

NSRecursiveLock。递归锁,允许同一线程重复获取。适合递归场景。性能约 250ns。

NSOperationQueue。基于 GCD 的操作队列,支持操作依赖和并发控制。适合复杂任务编排。

性能对比:os_unfair_lock(30ns)< dispatch_semaphore(50ns)< dispatch_queue(100ns)< pthread_mutex(100ns)< NSRecursiveLock(250ns)< NSLock(200ns)。推荐:性能敏感用 os_unfair_lock 或 dispatch_semaphore,复杂场景用 GCD 串行队列。


Q12: iOS 内存管理中的 AutoreleasePool 底层实现是什么?主线程和子线程中管理有何不同?

: AutoreleasePool 的底层实现:

底层数据结构。AutoreleasePool 实际上是 two pointers(begin 和 end 指针),封装在 NSAutoreleasePool 中。begin 指向 pool 的起始位置,end 指向最新添加的对象。每个线程维护一个 autorelease pool 栈(__ARC_release_per_thread 数组)。

添加对象到 pool。调用 [obj autorelease] 时,runtime 将对象指针写入当前线程的 pool 栈中 end 指向的位置,然后 end += sizeof(void*)。

pool 释放。调用 pool 的 drain 或 pop 方法时,遍历 begin 到 end 之间的所有对象,调用 release。如果对象数量为零则释放 pool 本身。

主线程自动管理。主线程的 RunLoop 在每个迭代中自动创建和释放 AutoreleasePool。具体流程:RunLoop 进入 kCFRunLoopBeforeSources 阶段时创建 pool,退出时释放 pool。这意味着主线程每处理一个事件就释放一次临时对象。

子线程手动管理。子线程不会自动创建 AutoreleasePool,需要手动管理。在子线程中使用大量临时对象时必须手动创建:@autoreleasepool { ... }。否则会累积到进程结束才释放,可能导致内存峰值过高。

性能分析。pool 创建约 50ns,释放约 200ns(与对象数量成线性关系)。大循环中手动管理 pool 可显著降低内存峰值。

10. 参考资源