高级 iOS 开发者面试问题与答案

Milad Bonakdar
作者
用实用问题准备高级 iOS 面试,覆盖 Swift、SwiftUI、架构、并发、性能、Core Data、离线同步和安全。
介绍
高级 iOS 面试考察的不只是 API 定义,而是你能否把 Swift 和 Apple 框架知识连接到真实产品决策中:架构、状态管理、并发、性能、持久化、安全,以及实际应用里的取舍。
用这份指南练习先给出判断,再解释要避免的失败模式,并说清楚会使用哪种 Xcode 工具或代码方案。高级回答应该能在代码评审中让团队信任,而不只是背诵概念。
高级 Swift & 语言特性 (6 个问题)
1. 解释 Swift 的内存管理和 ARC(自动引用计数)。
答案: ARC 通过跟踪和管理类实例的引用来自动管理内存。
- 工作原理: 每个类实例都有一个引用计数。当计数达到零时,该实例将被释放。
- 强引用 (Strong References): 默认。增加引用计数。
- 弱引用 (Weak References): 不增加引用计数。当实例被释放时,自动变为
nil。 - 无主引用 (Unowned References): 不增加引用计数,但假定该实例始终存在。
- 循环引用 (Retain Cycles): 当两个对象相互持有强引用时发生,从而阻止释放。
稀有度: 非常常见 难度: 困难
2. 什么是 Swift 中的泛型?为什么它们有用?
答案: 泛型允许你编写灵活、可重用的函数和类型,可以处理任何类型。
- 优点: 代码可重用性、类型安全、性能(没有运行时开销)
- 类型约束 (Type Constraints): 将泛型类型限制为特定的协议或类
- 关联类型 (Associated Types): 在协议中用于定义占位符类型
稀有度: 常见 难度: 中等
3. 解释 escaping 和 non-escaping 闭包之间的区别。
答案:
- Non-escaping (默认): 闭包在函数返回之前执行。编译器可以更好地优化。
- Escaping (
@escaping): 闭包的生命周期超过函数(存储在属性中,异步调用)。必须显式捕获self。
稀有度: 常见 难度: 中等
4. map、flatMap 和 compactMap 之间有什么区别?
答案: 这些是用于转换集合的高阶函数:
map: 转换每个元素并返回结果数组compactMap: 类似于map,但会过滤掉nil值flatMap: 将嵌套数组展平为单个数组
稀有度: 常见 难度: 简单
5. 解释 Swift 中的属性包装器 (Property Wrappers)。
答案: 属性包装器在管理属性存储方式的代码和定义属性的代码之间添加了一个分离层。
- 内置示例: SwiftUI 中的
@State、@Published、@AppStorage - 自定义包装器: 定义可重用的属性行为
稀有度: 中等 难度: 困难
6. 什么是 Result 类型?它如何使用?
答案:
Result 是一个枚举,表示成功或失败,使错误处理更加明确。
- 定义:
enum Result<Success, Failure: Error> - 优点: 类型安全的错误处理、更清晰的 API 约定、比异步代码的抛出函数更好
稀有度: 常见 难度: 中等
架构模式 (5 个问题)
7. 解释 MVVM(模型-视图-视图模型)模式。
答案: MVVM 将 UI 逻辑与业务逻辑分离,使代码更易于测试和维护。
- 模型: 数据和业务逻辑
- 视图: UI (UIViewController, SwiftUI View)
- 视图模型: 表示逻辑,转换模型数据以供视图使用
- 优点: 可测试(视图模型没有 UI 依赖项)、可重用的视图模型、清晰的关注点分离
稀有度: 非常常见 难度: 中等
8. 什么是协调器 (Coordinator) 模式?为什么要使用它?
答案: 协调器模式将导航逻辑与视图控制器分离。
- 问题: 大量的视图控制器,导航逻辑与 UI 逻辑混合在一起
- 解决方案: 协调器处理导航流程
- 优点: 可重用的视图控制器、可测试的导航、清晰的应用流程
稀有度: 中等 难度: 困难
9. 解释 iOS 中的依赖注入 (Dependency Injection)。
答案: 依赖注入是一种设计模式,其中依赖项被提供给对象,而不是在内部创建。
- 优点: 可测试性(注入模拟对象)、灵活性、松耦合
- 类型:
- 构造器注入 (Constructor Injection): 通过初始化器传递依赖项(最常见)
- 属性注入 (Property Injection): 在初始化后设置依赖项
- 方法注入 (Method Injection): 将依赖项作为方法参数传递
稀有度: 常见 难度: 中等
10. 什么是仓库 (Repository) 模式?
答案: 仓库模式抽象了数据访问逻辑,为数据操作提供了一个清晰的 API。
- 优点: 集中式数据逻辑、易于切换数据源(API、数据库、缓存)、可测试
- 实现: 仓库在多个数据源之间进行协调
稀有度: 中等 难度: 中等
11. 解释 MVC、MVP 和 MVVM 之间的区别。
答案:
- MVC (模型-视图-控制器):
- Apple 的默认模式
- 控制器在模型和视图之间进行调解
- 问题:大量的视图控制器
- MVP (模型-视图-表示器):
- 表示器处理所有 UI 逻辑
- 视图是被动的(仅显示数据)
- 比 MVC 更好的可测试性
- MVVM (模型-视图-视图模型):
- 视图模型公开数据流
- 视图绑定到视图模型
- 最适合反应式编程 (Combine, RxSwift)
稀有度: 常见 难度: 困难
性能与优化 (5 个问题)
12. 如何优化表格视图 (table view) 和集合视图 (collection view) 的性能?
答案: 多种策略可以提高滚动性能:
- 单元格重用 (Cell Reuse): 正确使用
dequeueReusableCell - 避免繁重操作 (Avoid Heavy Operations): 不要在
cellForRowAt中执行昂贵的计算 - 图像优化 (Image Optimization):
- 将图像大小调整为显示大小
- 使用后台线程进行图像处理
- 缓存解码后的图像
- 预取 (Prefetching): 实现
UITableViewDataSourcePrefetching - 高度缓存 (Height Caching): 缓存计算的单元格高度
- 避免透明度 (Avoid Transparency): 不透明视图渲染速度更快
稀有度: 非常常见 难度: 中等
13. 解释 Instruments 以及如何使用它们进行性能分析。
答案: Instruments 是 Xcode 的性能分析工具。
- 常用 Instruments:
- Time Profiler: 识别 CPU 密集型代码
- Allocations: 跟踪内存分配和泄漏
- Leaks: 检测内存泄漏
- Network: 监控网络活动
- Energy Log: 分析电池使用情况
- 工作流程:
- 分析应用 (Cmd+I)
- 选择 instrument
- 记录并与应用交互
- 分析调用树和时间线
- 识别瓶颈
稀有度: 常见 难度: 中等
14. 如何检测和修复内存泄漏?
答案: 当不再需要的对象未被释放时,会发生内存泄漏。
- 常见原因:
- 循环引用(强引用循环)
- 闭包强烈捕获
self - 未标记为
weak的委托
- 检测:
- Instruments Leaks 工具
- Xcode 中的内存图调试器
- 注意内存使用量增加
- 修复:
- 对委托使用
weak或unowned - 在闭包中使用
[weak self]或[unowned self] - 打破循环引用
- 对委托使用
稀有度: 非常常见 难度: 中等
15. 你使用哪些技术来优化应用启动?
答案: 更快的应用启动可以改善用户体验:
- 延迟加载 (Lazy Loading): 仅在需要时才初始化对象
- 减少 Dylib 加载 (Reduce Dylib Loading): 最小化动态库
- 优化
application:didFinishLaunching:- 将非关键工作移至后台
- 延迟繁重的初始化
- 二进制大小 (Binary Size): 较小的二进制文件加载速度更快
- 避免繁重操作 (Avoid Heavy Operations): 不要阻塞主线程
- 测量 (Measure): 使用 Instruments 的 App Launch 模板
稀有度: 常见 难度: 中等
16. 你如何处理图像缓存和加载?
答案: 高效的图像处理对于性能至关重要:
- 策略:
- 内存缓存 (Memory Cache): 快速访问,大小有限
- 磁盘缓存 (Disk Cache): 持久性,容量更大
- 下载 (Download): 从网络获取
- 库: SDWebImage, Kingfisher(自动处理缓存)
- 自定义实现:
稀有度: 常见 难度: 困难
并发 & 异步编程 (4 个问题)
17. 解释 Swift 中的 async/await。
答案: Swift 5.5 中引入的 Swift 现代并发模型。
- 优点: 比完成处理程序更清晰的语法、更简单的错误处理、编译器强制执行的线程安全
- 关键字:
async:标记一个可以挂起的函数await:标记一个挂起点Task:创建一个新的异步上下文actor:线程安全的引用类型
稀有度: 非常常见 难度: 困难
18. 什么是 Swift 中的 Actors?
答案: Actors 是引用类型,可以保护其可变状态免受数据竞争的影响。
- 线程安全: 一次只能有一个任务访问 actor 的可变状态
- 自动同步: 编译器强制执行安全访问
- Main Actor: 用于 UI 更新的特殊 actor
稀有度: 中等 难度: 困难
19. 解释 Combine 框架。
答案: Combine 是 Apple 的反应式编程框架。
- 核心概念:
- Publisher: 随时间推移发出值
- Subscriber: 接收值
- Operator: 转换值
- 优点: 声明式、可组合、内置运算符
- 用例: 网络、用户输入处理、数据绑定
稀有度: 常见 难度: 困难
20. 串行 (Serial) 队列和并发 (Concurrent) 队列之间有什么区别?
答案: 调度队列 (Dispatch queues) 串行或并发地执行任务:
- 串行队列: 一次按 FIFO 顺序执行一个任务。 任务等待前一个任务完成。
- 并发队列: 同时执行多个任务。 任务按 FIFO 顺序启动,但可以按任何顺序完成。
- 主队列: 用于 UI 更新的特殊串行队列
稀有度: 常见 难度: 中等
Core Data & 持久性 (3 个问题)
21. 解释 Core Data 架构及其主要组成部分。
答案: Core Data 是 Apple 的对象图和持久性框架。
- NSManagedObjectModel: 模式定义(实体、属性、关系)
- NSPersistentStoreCoordinator: 协调上下文和存储之间的关系
- NSManagedObjectContext: 对象的 工作内存(像草稿纸)
- NSPersistentStore: 实际存储(SQLite、二进制、内存中)
稀有度: 常见 难度: 中等
22. 如何处理 Core Data 中的并发?
答案: Core Data 上下文不是线程安全的。 使用正确的并发模式:
- 上下文类型:
- 主队列上下文: 用于 UI 操作
- 私有队列上下文: 用于后台操作
- 最佳实践:
- 永远不要在线程之间传递托管对象
- 对上下文操作使用
perform或performAndWait - 在上下文之间传递对象 ID
稀有度: 中等 难度: 困难
23. 什么是 NSFetchedResultsController?何时使用它?
答案:
NSFetchedResultsController 可以有效地管理用于表格/集合视图的 Core Data 结果。
- 优点:
- 自动更改跟踪
- 内存高效(批处理)
- 部分管理
- 自动 UI 更新
- 用例: 在表格/集合视图中显示 Core Data 对象
稀有度: 中等 难度: 中等
系统设计 & 最佳实践 (2 个问题)
24. 你将如何设计一个离线优先的移动应用?
答案: 离线优先应用在没有互联网的情况下工作,并在连接时同步。
- 架构:
- 将本地数据库作为事实来源(Core Data、Realm)
- 同步层与服务器协调
- 冲突解决策略
- 策略:
- 乐观 UI: 立即显示更改,在后台同步
- 队列操作: 存储失败的请求,在在线时重试
- 时间戳/版本: 跟踪数据新鲜度
- 挑战:
- 冲突解决(后写优先、合并、用户选择)
- 数据一致性
- 存储限制
稀有度: 中等 难度: 困难
25. 你在 iOS 中有哪些应用安全策略?
答案: 多层保护应用和用户数据:
- 数据保护:
- 用于敏感数据的 Keychain(密码、令牌)
- 加密静态数据
- 使用数据保护 API
- 网络安全:
- 仅 HTTPS (App Transport Security)
- 用于关键 API 的证书固定 (certificate pinning)
- 验证 SSL 证书
- 代码安全:
- 用于敏感逻辑的混淆
- 越狱检测
- 没有硬编码的机密
- 身份验证:
- 生物识别身份验证(面容 ID、触控 ID)
- 安全令牌存储
- 令牌刷新机制
- 输入验证:
- 清理用户输入
- 阻止注入攻击
稀有度: 常见 难度: 困难


