3. iOS基础核心:内存管理、Runtime、Runloop
章节聚焦:内存管理、Runtime、Runloop 是 iOS 面试的“三座大山”,掌握核心原理与实战经验,能覆盖 80% 的中高级题目。本单元提供结构化问答框架与真实项目模板。
🎯 面试问题:谈谈 iOS 内存管理机制,包括 ARC 和循环引用
| 考察点 | ARC 理解、weak/strong 原理、循环引用场景与解决方案、dealloc 流程 |
| 回答框架 | ① 简述 ARC/MRC 区别,引用计数原理;② 列举循环引用典型场景(Block、Delegate、NSTimer);③ 给出对应方案(weak-strong-dance、weak 修饰、invalidate);④ 补充 weak 底层(SideTable、置 nil) |
| 示例回答 | ARC 在编译期自动插入 retain/release,对象引用计数归零时 dealloc。循环引用常见于 Block 强引用 self、delegate 未用 weak。解决方案:Block 内用 weakSelf + strongSelf,delegate 用 weak,NSTimer 提前 invalidate。weak 指针不增加计数,对象释放时通过 SideTable 自动置 nil。 |
| 可能追问 | weak 底层如何实现?dealloc 触发顺序?自动释放池在 ARC 中的角色? |
| 避坑提醒 | ⚠️ 不要滥用 weak,频繁访问 weak 会触发 runtime 查表;NSTimer 的 target 强引用是经典隐蔽循环引用,必须手动 invalidate。 |
🎯 面试问题:说说 Runtime 的消息转发机制,以及你的实际应用
| 考察点 | 消息发送流程、动态方法解析、快速转发、完整转发、Method Swizzling、关联对象 |
| 回答框架 | ① 简述消息发送(isa->superclass->IMP 缓存);② 三级转发流程:resolveInstanceMethod → forwardingTargetForSelector → forwardInvocation;③ 实际应用举例:AOP 统计、动态添加方法、JSPatch 热修复 |
| 示例回答 | 消息发送阶段查找缓存和类方法列表。若找不到则触发三级转发:第一级动态添加方法(resolveInstanceMethod:),第二级将消息转给其他对象(forwardingTargetForSelector:),第三级封装 NSInvocation 自定义处理。我在项目中用 Method Swizzling 统计 UIViewController 的 viewDidAppear 次数,实现无侵入埋点。 |
| 可能追问 | Method Swizzling 的线程安全问题?+load 和 +initialize 的区别?关联对象内存管理? |
| 避坑提醒 | ⚠️ Swizzling 应在 +load 中执行且 dispatch_once 保证唯一;避免多次 Swizzle 同一个方法;关联对象不自动释放,需在 dealloc 中清理。 |
🎯 面试问题:请解释 Runloop 的工作原理,以及如何在项目中利用它
| 考察点 | Runloop 模型、Mode 切换、Source/Timer/Observer、自动释放池、子线程 Runloop |
| 回答框架 | ① Runloop 是事件循环,每个线程对应一个;② 包含多种 Mode(Default、Tracking、Common)和输入源(Source0、Source1、Timer、Observer);③ 应用:空闲时预加载(Observer)、滑动时暂停 Timer(Mode 切换)、子线程保活 |
| 示例回答 | Runloop 在循环中检查端口、计时器、输入源并分派事件。主线程 Runloop 默认运行在 NSDefaultRunLoopMode,滑动时切换到 UITrackingRunLoopMode,导致 NSTimer 暂停。我在项目中利用 CFRunLoopObserver 在空闲阶段执行图片预下载,避免卡顿;同时将 Timer 加入 NSRunLoopCommonModes 保证滑动时继续触发。 |
| 可能追问 | 自动释放池在 Runloop 中何时创建和销毁?如何管理子线程 Runloop?performSelector:withObject:afterDelay: 的原理? |
| 避坑提醒 | ⚠️ 子线程 Runloop 默认不开启,需调用 [runLoop run];不要在 Runloop 中执行耗时同步操作,会阻塞事件处理;NSTimer 必须加入 Runloop 才能生效。 |
📁 项目案例:首页滑动场景内存优化
| 项目名称 | 社交 App 首页 Feed 流内存优化 |
| 业务背景 | 首页信息流滑动时内存暴涨,出现 OOM 崩溃,严重影响用户体验 |
| 规模指标 | DAU 500万,首页每屏展示6~10条动态,图文+视频混合 |
| 技术栈 | Objective-C + Swift 混编,SDWebImage、IGListKit、AVPlayer |
| 核心难点 | ① 图片缓存策略不当导致 UIImage 长期驻留;② Cell 复用中 Block 强引用形成循环引用;③ 视频播放器未及时释放,内存泄漏 |
| 面试高频追问 | 如何用 Instruments 定位循环引用?NSCache 与 NSDictionary 区别?图片缓存如何控制占用峰值?视频播放器泄漏的典型根因? |
| 可迁移回答话术 | 通过 Instruments Allocations 发现大量 UIImage 未释放,定位到 SDWebImage 缓存无上限和 Block 强引用。改用 NSCache 并设置 countLimit,配合 dealloc 中清理资源(移除 KVO、停止播放),最终内存峰值降低 40%。该方案适用于任何缓存+异步加载场景,核心是“缓存上限 + 引用链断掉 + 手动清理”。 |
💡 复习提示:本章三个核心主题紧密关联——内存管理依赖 Runtime 的引用计数实现,Runloop 影响自动释放池和异步内存释放。面试时注意展现“原理→场景→方案”的完整链条。