3. iOS基础核心:内存管理、Runtime、Runloop

章节聚焦:内存管理、Runtime、Runloop 是 iOS 面试的“三座大山”,掌握核心原理与实战经验,能覆盖 80% 的中高级题目。本单元提供结构化问答框架与真实项目模板。
ARC vs MRC 循环引用场景 weak 底层原理 内存优化工具 检测 & 修复 Block / Delegate / NSTimer

🎯 面试问题:谈谈 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。
方法未实现 动态方法解析 备用接收者 完整消息转发 resolveInstanceMethod: forwardingTargetForSelector: forwardInvocation:

🎯 面试问题:说说 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 影响自动释放池和异步内存释放。面试时注意展现“原理→场景→方案”的完整链条。