十二月 21, 2025
47 分钟阅读

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

interview
career-advice
job-search
高级 iOS 开发者面试问题与答案
Milad Bonakdar

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): 当两个对象相互持有强引用时发生,从而阻止释放。
class Person {
    var name: String
    var apartment: Apartment?
    
    init(name: String) { self.name = name }
    deinit { print("\(name) 正在被释放") }
}

class Apartment {
    var unit: String
    weak var tenant: Person?  // weak 用于打破循环引用
    
    init(unit: String) { self.unit = unit }
    deinit { print("公寓 \(unit) 正在被释放") }
}

稀有度: 非常常见 难度: 困难


2. 什么是 Swift 中的泛型?为什么它们有用?

答案: 泛型允许你编写灵活、可重用的函数和类型,可以处理任何类型。

  • 优点: 代码可重用性、类型安全、性能(没有运行时开销)
  • 类型约束 (Type Constraints): 将泛型类型限制为特定的协议或类
  • 关联类型 (Associated Types): 在协议中用于定义占位符类型
// 泛型函数
func swap<T>(_ a: inout T, _ b: inout T) {
    let temp = a
    a = b
    b = temp
}

// 泛型类型
struct Stack<Element> {
    private var items: [Element] = []
    
    mutating func push(_ item: Element) {
        items.append(item)
    }
    
    mutating func pop() -> Element? {
        return items.popLast()
    }
}

// 类型约束
func findIndex<T: Equatable>(of valueToFind: T, in array: [T]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}

稀有度: 常见 难度: 中等


3. 解释 escapingnon-escaping 闭包之间的区别。

答案:

  • Non-escaping (默认): 闭包在函数返回之前执行。编译器可以更好地优化。
  • Escaping (@escaping): 闭包的生命周期超过函数(存储在属性中,异步调用)。必须显式捕获 self
class NetworkManager {
    var completionHandlers: [() -> Void] = []
    
    // Escaping 闭包 - 存储以供稍后使用
    func fetchData(completion: @escaping (Data?) -> Void) {
        URLSession.shared.dataTask(with: url) { data, _, _ in
            completion(data)  // 在函数返回后调用
        }.resume()
    }
    
    // Non-escaping 闭包 - 立即执行
    func processData(_ data: Data, transform: (Data) -> String) -> String {
        return transform(data)  // 在函数返回之前调用
    }
}

稀有度: 常见 难度: 中等


4. mapflatMapcompactMap 之间有什么区别?

答案: 这些是用于转换集合的高阶函数:

  • map 转换每个元素并返回结果数组
  • compactMap 类似于 map,但会过滤掉 nil
  • flatMap 将嵌套数组展平为单个数组
let numbers = [1, 2, 3, 4, 5]

// map
let doubled = numbers.map { $0 * 2 }
// [2, 4, 6, 8, 10]

// compactMap - 移除 nil
let strings = ["1", "2", "three", "4"]
let validNumbers = strings.compactMap { Int($0) }
// [1, 2, 4]

// flatMap - 展平数组
let nested = [[1, 2], [3, 4], [5]]
let flattened = nested.flatMap { $0 }
// [1, 2, 3, 4, 5]

稀有度: 常见 难度: 简单


5. 解释 Swift 中的属性包装器 (Property Wrappers)。

答案: 属性包装器在管理属性存储方式的代码和定义属性的代码之间添加了一个分离层。

  • 内置示例: SwiftUI 中的 @State@Published@AppStorage
  • 自定义包装器: 定义可重用的属性行为
@propertyWrapper
struct Capitalized {
    private var value: String = ""
    
    var wrappedValue: String {
        get { value }
        set { value = newValue.capitalized }
    }
}

struct User {
    @Capitalized var firstName: String
    @Capitalized var lastName: String
}

var user = User()
user.firstName = "john"
print(user.firstName)  // "John"

// UserDefaults 包装器
@propertyWrapper
struct UserDefault<T> {
    let key: String
    let defaultValue: T
    
    var wrappedValue: T {
        get { UserDefaults.standard.object(forKey: key) as? T ?? defaultValue }
        set { UserDefaults.standard.set(newValue, forKey: key) }
    }
}

struct Settings {
    @UserDefault(key: "username", defaultValue: "Guest")
    static var username: String
}

稀有度: 中等 难度: 困难


6. 什么是 Result 类型?它如何使用?

答案: Result 是一个枚举,表示成功或失败,使错误处理更加明确。

  • 定义: enum Result<Success, Failure: Error>
  • 优点: 类型安全的错误处理、更清晰的 API 约定、比异步代码的抛出函数更好
enum NetworkError: Error {
    case invalidURL
    case noData
    case decodingError
}

func fetchUser(id: Int, completion: @escaping (Result<User, NetworkError>) -> Void) {
    guard let url = URL(string: "https://api.example.com/users/\(id)") else {
        completion(.failure(.invalidURL))
        return
    }
    
    URLSession.shared.dataTask(with: url) { data, response, error in
        guard let data = data else {
            completion(.failure(.noData))
            return
        }
        
        do {
            let user = try JSONDecoder().decode(User.self, from: data)
            completion(.success(user))
        } catch {
            completion(.failure(.decodingError))
        }
    }.resume()
}

// 用法
fetchUser(id: 1) { result in
    switch result {
    case .success(let user):
        print("用户:\(user.name)")
    case .failure(let error):
        print("错误:\(error)")
    }
}

稀有度: 常见 难度: 中等


架构模式 (5 个问题)

7. 解释 MVVM(模型-视图-视图模型)模式。

答案: MVVM 将 UI 逻辑与业务逻辑分离,使代码更易于测试和维护。

Loading diagram...
  • 模型: 数据和业务逻辑
  • 视图: UI (UIViewController, SwiftUI View)
  • 视图模型: 表示逻辑,转换模型数据以供视图使用
  • 优点: 可测试(视图模型没有 UI 依赖项)、可重用的视图模型、清晰的关注点分离
// 模型
struct User {
    let id: Int
    let firstName: String
    let lastName: String
}

// 视图模型
class UserViewModel {
    private let user: User
    
    var fullName: String {
        "\(user.firstName) \(user.lastName)"
    }
    
    var displayText: String {
        "用户 #\(user.id): \(fullName)"
    }
    
    init(user: User) {
        self.user = user
    }
}

// 视图
class UserViewController: UIViewController {
    private let viewModel: UserViewModel
    private let label = UILabel()
    
    init(viewModel: UserViewModel) {
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: nil)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        label.text = viewModel.displayText
    }
}

稀有度: 非常常见 难度: 中等


8. 什么是协调器 (Coordinator) 模式?为什么要使用它?

答案: 协调器模式将导航逻辑与视图控制器分离。

  • 问题: 大量的视图控制器,导航逻辑与 UI 逻辑混合在一起
  • 解决方案: 协调器处理导航流程
  • 优点: 可重用的视图控制器、可测试的导航、清晰的应用流程
protocol Coordinator {
    var navigationController: UINavigationController { get }
    func start()
}

class AppCoordinator: Coordinator {
    let navigationController: UINavigationController
    
    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }
    
    func start() {
        showLogin()
    }
    
    func showLogin() {
        let loginVC = LoginViewController()
        loginVC.coordinator = self
        navigationController.pushViewController(loginVC, animated: true)
    }
    
    func showHome() {
        let homeVC = HomeViewController()
        homeVC.coordinator = self
        navigationController.pushViewController(homeVC, animated: true)
    }
}

稀有度: 中等 难度: 困难


9. 解释 iOS 中的依赖注入 (Dependency Injection)。

答案: 依赖注入是一种设计模式,其中依赖项被提供给对象,而不是在内部创建。

  • 优点: 可测试性(注入模拟对象)、灵活性、松耦合
  • 类型:
    • 构造器注入 (Constructor Injection): 通过初始化器传递依赖项(最常见)
    • 属性注入 (Property Injection): 在初始化后设置依赖项
    • 方法注入 (Method Injection): 将依赖项作为方法参数传递
// 用于抽象的协议
protocol NetworkService {
    func fetchData(completion: @escaping (Result<Data, Error>) -> Void)
}

// 具体实现
class URLSessionNetworkService: NetworkService {
    func fetchData(completion: @escaping (Result<Data, Error>) -> Void) {
        // 真实的網絡呼叫
    }
}

// 用于测试的模拟对象
class MockNetworkService: NetworkService {
    func fetchData(completion: @escaping (Result<Data, Error>) -> Void) {
        completion(.success(Data()))
    }
}

// 具有依赖注入的视图模型
class UserViewModel {
    private let networkService: NetworkService
    
    // 构造器注入
    init(networkService: NetworkService) {
        self.networkService = networkService
    }
    
    func loadUsers() {
        networkService.fetchData { result in
            // 处理结果
        }
    }
}

// 用法
let viewModel = UserViewModel(networkService: URLSessionNetworkService())

// 测试
let testViewModel = UserViewModel(networkService: MockNetworkService())

稀有度: 常见 难度: 中等


10. 什么是仓库 (Repository) 模式?

答案: 仓库模式抽象了数据访问逻辑,为数据操作提供了一个清晰的 API。

  • 优点: 集中式数据逻辑、易于切换数据源(API、数据库、缓存)、可测试
  • 实现: 仓库在多个数据源之间进行协调
protocol UserRepository {
    func getUser(id: Int) async throws -> User
    func saveUser(_ user: User) async throws
}

class UserRepositoryImpl: UserRepository {
    private let apiService: APIService
    private let cacheService: CacheService
    
    init(apiService: APIService, cacheService: CacheService) {
        self.apiService = apiService
        self.cacheService = cacheService
    }
    
    func getUser(id: Int) async throws -> User {
        // 首先检查缓存
        if let cachedUser = try? await cacheService.getUser(id: id) {
            return cachedUser
        }
        
        // 从 API 获取
        let user = try await apiService.fetchUser(id: id)
        
        // 更新缓存
        try? await cacheService.saveUser(user)
        
        return user
    }
    
    func saveUser(_ user: User) async throws {
        try await apiService.updateUser(user)
        try await cacheService.saveUser(user)
    }
}

稀有度: 中等 难度: 中等


11. 解释 MVC、MVP 和 MVVM 之间的区别。

答案:

  • MVC (模型-视图-控制器):
    • Apple 的默认模式
    • 控制器在模型和视图之间进行调解
    • 问题:大量的视图控制器
  • MVP (模型-视图-表示器):
    • 表示器处理所有 UI 逻辑
    • 视图是被动的(仅显示数据)
    • 比 MVC 更好的可测试性
  • MVVM (模型-视图-视图模型):
    • 视图模型公开数据流
    • 视图绑定到视图模型
    • 最适合反应式编程 (Combine, RxSwift)

稀有度: 常见 难度: 困难


性能与优化 (5 个问题)

12. 如何优化表格视图 (table view) 和集合视图 (collection view) 的性能?

答案: 多种策略可以提高滚动性能:

  1. 单元格重用 (Cell Reuse): 正确使用 dequeueReusableCell
  2. 避免繁重操作 (Avoid Heavy Operations): 不要在 cellForRowAt 中执行昂贵的计算
  3. 图像优化 (Image Optimization):
    • 将图像大小调整为显示大小
    • 使用后台线程进行图像处理
    • 缓存解码后的图像
  4. 预取 (Prefetching): 实现 UITableViewDataSourcePrefetching
  5. 高度缓存 (Height Caching): 缓存计算的单元格高度
  6. 避免透明度 (Avoid Transparency): 不透明视图渲染速度更快
class ImageTableViewCell: UITableViewCell {
    static let identifier = "ImageCell"
    private let customImageView = UIImageView()
    
    override func prepareForReuse() {
        super.prepareForReuse()
        customImageView.image = nil  // 清除旧图像
    }
}

// 在视图控制器中
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: ImageTableViewCell.identifier, for: indexPath) as! ImageTableViewCell
    
    // 异步加载图像
    DispatchQueue.global(qos: .userInitiated).async {
        let image = self.loadAndResizeImage(at: indexPath)
        DispatchQueue.main.async {
            cell.imageView?.image = image
        }
    }
    
    return cell
}

稀有度: 非常常见 难度: 中等


13. 解释 Instruments 以及如何使用它们进行性能分析。

答案: Instruments 是 Xcode 的性能分析工具。

  • 常用 Instruments:
    • Time Profiler: 识别 CPU 密集型代码
    • Allocations: 跟踪内存分配和泄漏
    • Leaks: 检测内存泄漏
    • Network: 监控网络活动
    • Energy Log: 分析电池使用情况
  • 工作流程:
    1. 分析应用 (Cmd+I)
    2. 选择 instrument
    3. 记录并与应用交互
    4. 分析调用树和时间线
    5. 识别瓶颈

稀有度: 常见 难度: 中等


14. 如何检测和修复内存泄漏?

答案: 当不再需要的对象未被释放时,会发生内存泄漏。

  • 常见原因:
    • 循环引用(强引用循环)
    • 闭包强烈捕获 self
    • 未标记为 weak 的委托
  • 检测:
    • Instruments Leaks 工具
    • Xcode 中的内存图调试器
    • 注意内存使用量增加
  • 修复:
    • 对委托使用 weakunowned
    • 在闭包中使用 [weak self][unowned self]
    • 打破循环引用
class ViewController: UIViewController {
    var closure: (() -> Void)?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 坏 - 循环引用
        closure = {
            self.view.backgroundColor = .red
        }
        
        // 好 - weak self
        closure = { [weak self] in
            self?.view.backgroundColor = .red
        }
        
        // 好 - unowned self (如果 self 始终存在)
        closure = { [unowned self] in
            self.view.backgroundColor = .red
        }
    }
}

稀有度: 非常常见 难度: 中等


15. 你使用哪些技术来优化应用启动?

答案: 更快的应用启动可以改善用户体验:

  1. 延迟加载 (Lazy Loading): 仅在需要时才初始化对象
  2. 减少 Dylib 加载 (Reduce Dylib Loading): 最小化动态库
  3. 优化 application:didFinishLaunching
    • 将非关键工作移至后台
    • 延迟繁重的初始化
  4. 二进制大小 (Binary Size): 较小的二进制文件加载速度更快
  5. 避免繁重操作 (Avoid Heavy Operations): 不要阻塞主线程
  6. 测量 (Measure): 使用 Instruments 的 App Launch 模板
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    
    // 关键:设置窗口和根视图控制器
    setupWindow()
    
    // 延迟非关键工作
    DispatchQueue.main.async {
        self.setupAnalytics()
        self.setupCrashReporting()
    }
    
    // 后台工作
    DispatchQueue.global(qos: .background).async {
        self.preloadData()
    }
    
    return true
}

稀有度: 常见 难度: 中等


16. 你如何处理图像缓存和加载?

答案: 高效的图像处理对于性能至关重要:

  • 策略:
    • 内存缓存 (Memory Cache): 快速访问,大小有限
    • 磁盘缓存 (Disk Cache): 持久性,容量更大
    • 下载 (Download): 从网络获取
  • 库: SDWebImage, Kingfisher(自动处理缓存)
  • 自定义实现:
class ImageCache {
    static let shared = ImageCache()
    
    private let memoryCache = NSCache<NSString, UIImage>()
    private let fileManager = FileManager.default
    private let cacheDirectory: URL
    
    private init() {
        cacheDirectory = fileManager.urls(for: .cachesDirectory, in: .userDomainMask)[0]
            .appendingPathComponent("ImageCache")
        try? fileManager.createDirectory(at: cacheDirectory, withIntermediateDirectories: true)
    }
    
    func loadImage(from url: URL, completion: @escaping (UIImage?) -> Void) {
        let key = url.absoluteString as NSString
        
        // 检查内存缓存
        if let cachedImage = memoryCache.object(forKey: key) {
            completion(cachedImage)
            return
        }
        
        // 检查磁盘缓存
        let fileURL = cacheDirectory.appendingPathComponent(url.lastPathComponent)
        if let diskImage = UIImage(contentsOfFile: fileURL.path) {
            memoryCache.setObject(diskImage, forKey: key)
            completion(diskImage)
            return
        }
        
        // 下载
        URLSession.shared.dataTask(with: url) { data, _, _ in
            guard let data = data, let image = UIImage(data: data) else {
                completion(nil)
                return
            }
            
            // 保存到缓存
            self.memoryCache.setObject(image, forKey: key)
            try? data.write(to: fileURL)
            
            DispatchQueue.main.async {
                completion(image)
            }
        }.resume()
    }
}

稀有度: 常见 难度: 困难


并发 & 异步编程 (4 个问题)

17. 解释 Swift 中的 async/await。

答案: Swift 5.5 中引入的 Swift 现代并发模型。

  • 优点: 比完成处理程序更清晰的语法、更简单的错误处理、编译器强制执行的线程安全
  • 关键字:
    • async:标记一个可以挂起的函数
    • await:标记一个挂起点
    • Task:创建一个新的异步上下文
    • actor:线程安全的引用类型
// 旧方式,使用完成处理程序
func fetchUser(id: Int, completion: @escaping (Result<User, Error>) -> Void) {
    URLSession.shared.dataTask(with: url) { data, response, error in
        // 处理结果
    }.resume()
}

// 新方式,使用 async/await
func fetchUser(id: Int) async throws -> User {
    let (data, _) = try await URLSession.shared.data(from: url)
    let user = try JSONDecoder().decode(User.self, from: data)
    return user
}

// 用法
Task {
    do {
        let user = try await fetchUser(id: 1)
        print(user.name)
    } catch {
        print("错误:\(error)")
    }
}

// 并行执行
async let user1 = fetchUser(id: 1)
async let user2 = fetchUser(id: 2)
let users = try await [user1, user2]

稀有度: 非常常见 难度: 困难


18. 什么是 Swift 中的 Actors?

答案: Actors 是引用类型,可以保护其可变状态免受数据竞争的影响。

  • 线程安全: 一次只能有一个任务访问 actor 的可变状态
  • 自动同步: 编译器强制执行安全访问
  • Main Actor: 用于 UI 更新的特殊 actor
actor BankAccount {
    private var balance: Double = 0
    
    func deposit(amount: Double) {
        balance += amount
    }
    
    func withdraw(amount: Double) -> Bool {
        guard balance >= amount else { return false }
        balance -= amount
        return true
    }
    
    func getBalance() -> Double {
        return balance
    }
}

// 用法
let account = BankAccount()

Task {
    await account.deposit(amount: 100)
    let balance = await account.getBalance()
    print("余额:\(balance)")
}

// Main Actor 用于 UI 更新
@MainActor
class ViewModel: ObservableObject {
    @Published var data: [String] = []
    
    func loadData() async {
        let newData = await fetchDataFromAPI()
        // 自动在主线程上
        self.data = newData
    }
}

稀有度: 中等 难度: 困难


19. 解释 Combine 框架。

答案: Combine 是 Apple 的反应式编程框架。

  • 核心概念:
    • Publisher: 随时间推移发出值
    • Subscriber: 接收值
    • Operator: 转换值
  • 优点: 声明式、可组合、内置运算符
  • 用例: 网络、用户输入处理、数据绑定
import Combine

class UserViewModel: ObservableObject {
    @Published var searchText = ""
    @Published var users: [User] = []
    
    private var cancellables = Set<AnyCancellable>()
    
    init() {
        $searchText
            .debounce(for: .milliseconds(300), scheduler: RunLoop.main)
            .removeDuplicates()
            .filter { !$0.isEmpty }
            .flatMap { query in
                self.searchUsers(query: query)
            }
            .receive(on: DispatchQueue.main)
            .sink { [weak self] users in
                self?.users = users
            }
            .store(in: &cancellables)
    }
    
    func searchUsers(query: String) -> AnyPublisher<[User], Never> {
        // 返回 publisher
        URLSession.shared.dataTaskPublisher(for: url)
            .map(\.data)
            .decode(type: [User].self, decoder: JSONDecoder())
            .replaceError(with: [])
            .eraseToAnyPublisher()
    }
}

稀有度: 常见 难度: 困难


20. 串行 (Serial) 队列和并发 (Concurrent) 队列之间有什么区别?

答案: 调度队列 (Dispatch queues) 串行或并发地执行任务:

  • 串行队列: 一次按 FIFO 顺序执行一个任务。 任务等待前一个任务完成。
  • 并发队列: 同时执行多个任务。 任务按 FIFO 顺序启动,但可以按任何顺序完成。
  • 主队列: 用于 UI 更新的特殊串行队列
// 串行队列
let serialQueue = DispatchQueue(label: "com.app.serial")
serialQueue.async { print("任务 1") }
serialQueue.async { print("任务 2") }
// 输出:任务 1,任务 2(始终按顺序)

// 并发队列
let concurrentQueue = DispatchQueue(label: "com.app.concurrent", attributes: .concurrent)
concurrentQueue.async { print("任务 A") }
concurrentQueue.async { print("任务 B") }
// 输出:任务 A,任务 B 或 任务 B,任务 A(顺序不保证)

// 用于写入操作的屏障
concurrentQueue.async(flags: .barrier) {
    // 独占访问 - 没有其他任务同时运行
    print("写入操作")
}

稀有度: 常见 难度: 中等


Core Data & 持久性 (3 个问题)

21. 解释 Core Data 架构及其主要组成部分。

答案: Core Data 是 Apple 的对象图和持久性框架。

Loading diagram...
  • NSManagedObjectModel: 模式定义(实体、属性、关系)
  • NSPersistentStoreCoordinator: 协调上下文和存储之间的关系
  • NSManagedObjectContext: 对象的 工作内存(像草稿纸)
  • NSPersistentStore: 实际存储(SQLite、二进制、内存中)

稀有度: 常见 难度: 中等


22. 如何处理 Core Data 中的并发?

答案: Core Data 上下文不是线程安全的。 使用正确的并发模式:

  • 上下文类型:
    • 主队列上下文: 用于 UI 操作
    • 私有队列上下文: 用于后台操作
  • 最佳实践:
    • 永远不要在线程之间传递托管对象
    • 对上下文操作使用 performperformAndWait
    • 在上下文之间传递对象 ID
class CoreDataManager {
    let persistentContainer: NSPersistentContainer
    
    var viewContext: NSManagedObjectContext {
        return persistentContainer.viewContext
    }
    
    func performBackgroundTask(_ block: @escaping (NSManagedObjectContext) -> Void) {
        persistentContainer.performBackgroundTask { context in
            block(context)
            
            if context.hasChanges {
                try? context.save()
            }
        }
    }
}

// 用法
coreDataManager.performBackgroundTask { context in
    let user = User(context: context)
    user.name = "John"
    // 上下文自动保存
}

稀有度: 中等 难度: 困难


23. 什么是 NSFetchedResultsController?何时使用它?

答案: NSFetchedResultsController 可以有效地管理用于表格/集合视图的 Core Data 结果。

  • 优点:
    • 自动更改跟踪
    • 内存高效(批处理)
    • 部分管理
    • 自动 UI 更新
  • 用例: 在表格/集合视图中显示 Core Data 对象
class UsersViewController: UITableViewController, NSFetchedResultsControllerDelegate {
    var fetchedResultsController: NSFetchedResultsController<User>!
    
    func setupFetchedResultsController() {
        let fetchRequest: NSFetchRequest<User> = User.fetchRequest()
        fetchRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
        
        fetchedResultsController = NSFetchedResultsController(
            fetchRequest: fetchRequest,
            managedObjectContext: context,
            sectionNameKeyPath: nil,
            cacheName: nil
        )
        
        fetchedResultsController.delegate = self
        try? fetchedResultsController.performFetch()
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return fetchedResultsController.sections?[section].numberOfObjects ?? 0
    }
    
    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        tableView.reloadData()
    }
}

稀有度: 中等 难度: 中等


系统设计 & 最佳实践 (2 个问题)

24. 你将如何设计一个离线优先的移动应用?

答案: 离线优先应用在没有互联网的情况下工作,并在连接时同步。

  • 架构:
    • 将本地数据库作为事实来源(Core Data、Realm)
    • 同步层与服务器协调
    • 冲突解决策略
  • 策略:
    • 乐观 UI: 立即显示更改,在后台同步
    • 队列操作: 存储失败的请求,在在线时重试
    • 时间戳/版本: 跟踪数据新鲜度
  • 挑战:
    • 冲突解决(后写优先、合并、用户选择)
    • 数据一致性
    • 存储限制

稀有度: 中等 难度: 困难


25. 你在 iOS 中有哪些应用安全策略?

答案: 多层保护应用和用户数据:

  1. 数据保护:
    • 用于敏感数据的 Keychain(密码、令牌)
    • 加密静态数据
    • 使用数据保护 API
  2. 网络安全:
    • 仅 HTTPS (App Transport Security)
    • 用于关键 API 的证书固定 (certificate pinning)
    • 验证 SSL 证书
  3. 代码安全:
    • 用于敏感逻辑的混淆
    • 越狱检测
    • 没有硬编码的机密
  4. 身份验证:
    • 生物识别身份验证(面容 ID、触控 ID)
    • 安全令牌存储
    • 令牌刷新机制
  5. 输入验证:
    • 清理用户输入
    • 阻止注入攻击
// Keychain 存储
class KeychainManager {
    func save(token: String) {
        let data = token.data(using: .utf8)!
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: "authToken",
            kSecValueData as String: data
        ]
        SecItemAdd(query as CFDictionary, nil)
    }
}

// 生物识别身份验证
import LocalAuthentication

func authenticateUser(completion: @escaping (Bool) -> Void) {
    let context = LAContext()
    var error: NSError?
    
    if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
        context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Authenticate to access app") { success, error in
            completion(success)
        }
    }
}

稀有度: 常见 难度: 困难


Newsletter subscription

真正有效的每周职业建议

将最新见解直接发送到您的收件箱

停止申请,开始被录用

使用全球求职者信赖的AI驱动优化,将您的简历转变为面试磁铁。

免费开始

分享这篇文章

让您的6秒钟发挥作用

招聘人员平均只花6到7秒扫描简历。我们经过验证的模板旨在立即吸引注意力并让他们继续阅读。