Swift 面试:ARC 与内存管理源码解析

Swift 面试:ARC 与内存管理源码解析 Swift 面试里问 ARC,通常不是想听一句“自动引用计数”。真正要答清楚的是:对象头里有什么、strong / weak / unowned 到底差在哪里、编译器什么时候插入或消除 retain/release、为什么闭包会循环引用。 这篇文章按面试题展开。先给可以直接回答的版本,再把结论落到 Swift 源码里的对象布局、引用计数状态机和 SIL ARC 优化。 面试高频问题 Swift 的 ARC 和 Objective-C 的 ARC 有什么区别? Swift 对象的引用计数存在哪里? strong、weak、unowned 的底层差异是什么? 为什么 weak 在对象释放后会自动变成 nil? 为什么 unowned 访问已释放对象会崩溃? 闭包为什么容易造成循环引用? 编译器会如何优化 retain/release? 值类型是否完全不参与 ARC? ARC 和 Copy-on-Write 有什么关系? 30 秒回答版 Swift ARC 是编译器和运行时共同完成的内存管理机制。 编译器在 SIL 阶段根据所有权语义插入 retain_value、release_value、strong_retain、strong_release 等引用计数指令,并通过 ARC 优化尽量消除冗余 retain/release。运行时负责真正维护对象的引用计数。 对原生 Swift 堆对象来说,对象头包含两部分:metadata 和 refCounts。源码里 HeapObject 的注释明确说 metadata 总是指向一个有效的 metadata 对象,refCounts 是 Swift heap object header 的非 Objective-C 成员。 ...

June 16, 2026

objc4 类加载、read_images 与类实现化

objc4 类加载、read_images 与类实现化 本文解释 Objective-C runtime 从 dyld 通知镜像映射,到读取类、处理 future class、realize class、methodize class、附着 category 的主干流程。 重点源码来自 runtime/objc-runtime-new.mm、objc-runtime-new.h、objc-os.mm、objc-class.mm 及相关测试。 作用 核心结构 关键流程 测试视角 一、这条链路的作用 把 Mach-O 中的静态元数据变成运行时可用的类 编译器把类、元类、方法列表、协议列表、属性列表和 ivar 布局写入 Mach-O 的 Objective-C sections。 这些数据起初主要是只读的 class_ro_t。runtime 加载镜像时要登记类名、修正 selector/protocol/class 引用,并在必要时分配可写的 class_rw_t。 延迟成本,同时保证消息发送能看到正确结构 大量类可以保持未 realized 状态,直到非懒加载、+load、消息发送、Swift 桥接或显式 API 需要它们。 realize 时才连接父类和元类、修正 ivar 偏移、初始化 cache、复制运行时标志,并把方法列表整理成可查找的形态。 简化理解:read_images 是“发现并登记”;readClass 是“读一个类声明并处理 future/remap”;realizeClassWithoutSwift 是“把类接入运行时继承图”;methodizeClass 是“准备方法/协议/属性并合并 category”。 二、核心类/结构和关键字段 结构 关键字段 作用 objc_class superclass、cache、bits 类对象本体。bits 初始可指向 class_ro_t,realize 后指向 class_rw_t 并夹带 fast flags。 class_ro_t flags、instanceStart、instanceSize、name、baseMethods、ivars 编译期只读描述。包含类名、方法、协议、属性、ivar 布局和 root/meta/future/realized 等 ABI 标志。 class_rw_t flags、ro_or_rw_ext、firstSubclass、nextSiblingClass 运行时可写状态。保存 realized/initialized/constructing 等状态,并把类接入子类链。 class_rw_ext_t ro、methods、properties、protocols 当类需要扩展列表时分配。category 附着后,方法/属性/协议列表通常进入这里。 method_list_t entsizeAndFlags、count、方法条目 方法列表。加载时会 uniquing selector,必要时排序并标记 fixed-up。 category_t cls、instanceMethods、classMethods、protocols、instanceProperties 分类元数据。目标类已 realized 时立即附着,否则暂存到 unattached categories。 header_info classlist、nlclslist、catlist、selrefs、protocolrefs 一个 Mach-O 镜像的 ObjC 元数据索引。objc-os.mm 中 addHeader 负责创建或取出它。 类数据从 ro 切换到 rw struct objc_class : objc_object { Class superclass; cache_t cache; class_data_bits_t bits; // 未实现时指向 class_ro_t,实现后指向 class_rw_t }; struct class_rw_t { uint32_t flags; // RW_REALIZED、RW_FUTURE、RW_INITIALIZED 等 explicit_atomic<uintptr_t> ro_or_rw_ext; Class firstSubclass; Class nextSiblingClass; }; class_data_bits_t::safe_ro() 允许在并发 realize 场景下安全取出 ro。 setData() 则在 realization 或构造阶段把 objc_class::bits 改成 rw 指针,并设置 FAST_IS_RW_POINTER。 ...

June 1, 2026

APM-B端平台设计

B 端 APM 平台的目标不是“展示所有数据”,而是让研发团队在最短路径内完成:发现问题、判断影响、定位原因、分配负责人、验证修复、防止再次劣化。 一、信息架构 flowchart TB Home["质量概览"] --> Stability["稳定性"] Home --> Performance["性能体验"] Home --> Network["网络"] Home --> Session["用户会话"] Home --> Release["版本发布"] Home --> Alert["告警中心"] Home --> Config["SDK 配置"] Stability --> Crash["Crash"] Stability --> Watchdog["Watchdog"] Stability --> FOOM["FOOM"] Performance --> Launch["启动"] Performance --> View["页面"] Performance --> Freeze["卡顿/掉帧"] Performance --> Resource["资源"] Release --> Gray["灰度对比"] Release --> Gate["发布门禁"] Alert --> Rule["规则"] Alert --> Notify["通知"] Alert --> Silence["静默"] 一级导航不要按技术实现命名,比如 Kafka、ClickHouse、符号化任务;应该按用户任务命名,比如稳定性、性能体验、网络、发布、告警。 二、首页质量概览 首页回答三个问题: 当前版本稳不稳? 过去一段时间有没有变差? 最应该处理的 Top 问题是什么? 核心卡片: ...

May 7, 2026

死锁(Deadlock)原理、常见场景与治理

死锁是 iOS 多线程开发中最隐蔽、也最致命的一类稳定性问题。它不像越界、空指针那样"立刻崩溃给你看",而是表现为主线程"卡住"——用户能感受到界面无响应、无法交互,最终被 iOS watchdog 强杀(0x8BADF00D),落在 Apple 崩溃日志里归为 EXC_CRASH (SIGKILL) 或卡死(Hang)。 本文系统梳理死锁的四个必要条件、iOS 生产环境中常见的发生场景,并结合 Apple 官方工具(TSan、Main Thread Checker、MetricKit)与业界最新实践(Sentry App Hangs、字节 Heimdallr、Swift Concurrency 的反思)讨论如何检测与治理。 一、死锁是什么 死锁(Deadlock)指两个或多个线程因争夺资源(锁、队列、信号量等)而互相等待,导致所有相关线程永久阻塞、无法推进的状态。 与死锁容易混淆的几个概念: 名称 核心特征 典型例子 死锁 Deadlock 多个线程循环等待对方持有的资源,永久阻塞 线程 A 持 lockA 等 lockB;线程 B 持 lockB 等 lockA 活锁 Livelock 线程不阻塞但也不推进,不停重试互相让步 两个线程都检测到冲突就回退重试,永远撞在一起 饥饿 Starvation 低优先级线程长期分不到资源 高优先级线程持续抢占 CPU,导致低优先级线程持锁永远运行不完 优先级反转 Priority Inversion 低优先级持锁 + 高优先级自旋等锁 + 中优先级抢 CPU,高优先级被间接阻塞 OSSpinLock 被废弃的根本原因 死循环 Busy Loop 单线程进入无限循环,CPU 占用 100% while(true) 忘记 break 卡顿 Hang 主线程执行时间超过阈值(几百 ms~几秒) 主线程做网络、解压大图、Core Data 大量 fetch 卡死 Watchdog 主线程卡住数秒(启动约 20s、前后台切换约 10s),触发系统强杀 死锁导致的卡死是最严重的一种 关键区别:死循环线程 CPU 占用高、处于 RUNNING 态;死锁线程 CPU 占用为 0、处于 WAITING/BLOCKED 态并被换出。这一点正是线上死锁自动判定的核心依据(见"检测"章节)。 ...

May 4, 2026

AFNetworking 源码导读

本文基于 AFNetworking 4.0.1(2020 年发布,仓库最后一次迭代)源码进行分析。AFNetworking 虽然已进入"稳定休眠"状态,但它作为 iOS 网络层的教科书级实现,其 NSURLSession 封装、HTTPS 校验、Multipart 流式上传、Method Swizzling 等设计思路至今仍值得每一位 iOS 工程师学习。源码仓库:AFNetworking/AFNetworking。 一、整体架构 AFNetworking 的整个库只有 7 个核心类,按职责划分为"核心会话"、“序列化”、“安全”、“可达性”、“UIKit 扩展"五个子模块(对应 CocoaPods Subspec 拆分): graph TB subgraph "核心会话层 (NSURLSession 封装)" A["AFURLSessionManagerNSURLSession 代理总线"] B["AFHTTPSessionManagerHTTP 便利方法 GET/POST/..."] C["AFURLSessionManagerTaskDelegate每个 Task 的代理持有者"] end subgraph "序列化层 (Serialization)" D["AFHTTPRequestSerializerURL 编码 / 头部 / User-Agent"] E["AFJSONRequestSerializerAFPropertyListRequestSerializer"] F["AFStreamingMultipartFormDataAFMultipartBodyStream (流式上传)"] G["AFHTTPResponseSerializer+ JSON/XML/Image/PropertyList"] end subgraph "安全层 (Security)" H["AFSecurityPolicySSL Pinning (None/Certificate/PublicKey)"] end subgraph "可达性层 (Reachability)" I["AFNetworkReachabilityManager基于 SCNetworkReachability"] end subgraph "UI 扩展 (UIKit+AFNetworking)" J["UIImageView+AFNetworkingUIButton+AFNetworkingUIActivityIndicatorView+AFNetworking ..."] end B --> A A --> C A --> D A --> G A --> H A --> I D --> E D --> F J --> B 源码目录一览(AFNetworking 4.0.1): ...

May 2, 2026

App启动流程

启动类型概览 iOS应用启动分为三种类型: 启动类型 描述 特点 冷启动(Cold Launch) App完全不在内存中,需要从头开始加载 耗时最长,需要完整执行所有启动流程 热启动(Warm Launch) App在后台被挂起(Suspended),重新进入前台 最快,只需恢复App状态,不需要重新创建进程 预热启动(Pre-warm Launch) 系统预测用户可能启动App,提前在后台执行部分启动流程 iOS 15+引入,介于冷启动和热启动之间 graph LR subgraph "冷启动(耗时最长)" A1[创建进程] --> A2[加载dylib] A2 --> A3[Rebase/Bind] A3 --> A4[+load] A4 --> A5[main] A5 --> A6[首帧渲染] end subgraph "预热启动(iOS 15+)" B1[系统提前完成(后台):创建进程 → 加载dylib → Rebase/Bind] --> B2[用户点击后执行:+load → main → 首帧渲染] end subgraph "热启动(最快)" C1[唤醒进程] --> C2[WillEnterForeground] C2 --> C3[DidBecomeActive] C3 --> C4[恢复UI] end 一、冷启动(Cold Launch) 冷启动是最完整的启动流程,也是启动优化的主要关注点。 冷启动流程概览 flowchart TD Start([App 冷启动流程]) --> PreMain subgraph PreMain["Pre-main 阶段"] direction TB A[加载可执行文件] --> B[加载动态库(包括 Swift Runtime)] B --> C[Rebase & Bind] C --> D[ObjC Runtime 初始化(类注册、Category 附加)] D --> D2[Swift Runtime 元数据注册(类型元数据、协议遵循表)] D2 --> D3[调用 +load 方法] D3 --> E[执行 Initializers(C++静态构造函数、__attribute__)] end PreMain --> MainPhase subgraph MainPhase["main 阶段"] direction TB F[main函数] --> G[UIApplicationMain] G --> H[AppDelegate回调] H --> I[首帧渲染(创建Window、RootViewController、首屏UI)] end MainPhase --> End([启动完成]) Pre-main 阶段 Pre-main阶段是指从用户点击App图标到main函数执行之前的过程,这个阶段主要由dyld(动态链接器)负责。 ...

May 2, 2026

Instruments详解

Instruments 是 Xcode 内置的性能分析套件,基于 DTrace/Apple Trace 基础设施构建,可以对 iOS、iPadOS、macOS、watchOS、tvOS、visionOS 的应用与系统进行 CPU、内存、图形、能耗、网络、I/O 等各个维度的观测。 Xcode 26(WWDC25)对 Instruments 做了近年来最大规模的升级,重点包括: Power Profiler:全新的能耗分析工具,支持 Tethered 与 Passive 两种录制模式。 下一代 SwiftUI instrument:基于 Cause & Effect Graph 可视化状态变更到视图更新的因果链。 Processor Trace:基于 Apple Silicon 硬件特性的"全量指令级"采集(Xcode 16.3 引入,26 完善)。 CPU Counters 重做:引入 Bottleneck Analysis 方法论与 CPU Bottlenecks 模板。 Foundation Models instrument:为 FoundationModels 框架提供 Prompt / Asset Loading / Inference 分阶段观测。 Animation Hitches 重做:修正多显示器场景下的统计,数据量显著减少,处理更快。 UI 重设计:主菜单精简、Settings 页面重写、Track 次级菜单、Launch/Attach 环境变量配置。 一、Instruments 基础 1.1 启动 Instruments 的几种方式 方式 使用场景 Xcode Product → Profile(⌘I) 日常开发,自动以 Release 模式编译并附加符号 Xcode Debug Navigator 中的图表 粗略观测 CPU / Memory / Disk / Network / Energy 直接打开 Instruments.app 对已安装的 App、系统进程或已录制的 .trace 文件进行分析 命令行 xcrun xctrace record CI / 自动化性能回归 设备端 Developer Settings 的 Power Profiler 离线、无 Mac 场景采集能耗数据 Xcode 26 中,Product → Profile 会按默认 scheme 的 Profile 构建配置(通常是 Release + -O),这与 Debug 模式的数据差异很大,做性能评估时必须用 Profile 构建。 ...

May 2, 2026

MVC架构详解

什么是MVC MVC(Model-View-Controller)是Apple官方推荐的iOS应用架构模式,也是最基础、最常见的架构。它将应用分为三个核心组件: Model(模型):负责数据和业务逻辑 View(视图):负责界面展示 Controller(控制器):负责协调Model和View MVC的结构(Apple MVC) graph TD V[ViewUIView] -->|用户事件| C[ControllerUIViewController] C -->|更新UI| V C <-->|读写数据| M[Model数据层] 在Apple的MVC中,View和Model之间不能直接通信,所有交互都必须通过Controller中转。 Model(模型层) Model负责: 数据的存储和管理 业务逻辑处理 网络请求和数据持久化 数据验证 // Model示例 struct User { let id: Int let name: String let email: String var isValidEmail: Bool { let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" let emailPredicate = NSPredicate(format: "SELF MATCHES %@", emailRegex) return emailPredicate.evaluate(with: email) } } class UserService { func fetchUser(id: Int, completion: @escaping (Result<User, Error>) -> Void) { // 网络请求逻辑 } } View(视图层) View负责: ...

May 2, 2026

编译优化-Bazel方案

当 iOS 工程的规模突破 CocoaPods / Xcode 原生构建体系的舒适区,Bazel 就成为下一代构建系统的首选。字节跳动的头条、抖音、Airbnb、Uber、Lyft、Pinterest、Square、Bilibili 等都已把 iOS 工程迁到 Bazel。本文介绍 Bazel 的核心原理、在 iOS 上的生态(rules_apple / rules_swift / rules_xcodeproj)以及大厂落地实践。 为什么是 Bazel CocoaPods + Xcode 在超大工程上的固有瓶颈: 粗粒度依赖:以 Pod / Target 为单位,增量编译颗粒粗 隐式依赖:Build Phases 隐含顺序,难以沙箱化 难以远程缓存:编译环境非 hermetic,hash 易变 难以远程执行:Xcode Build System 绑定本地 macOS Bazel 针对这些问题从设计之初就提供了: 能力 Bazel Xcode/CocoaPods 依赖粒度 文件级 Target 级 依赖显式化 必须声明 部分隐式 Sandbox 默认开启 无 远程缓存 原生 需第三方(ccache/Rugby) 远程执行 原生 不支持 跨语言 一流(Swift/OC/C++/Go/Rust/JS) 主要 Apple 平台 多平台 天生 iOS/macOS 为主 Bazel 核心概念 Workspace 与 Package WORKSPACE # 仓库根,声明外部依赖 foo/ ├── BUILD.bazel # package,本目录的构建声明 ├── main.swift └── util/ └── BUILD.bazel # 子 package Workspace:整个 Bazel 仓库,一个 WORKSPACE 文件一个仓 Package:任何包含 BUILD.bazel 的目录 Target:BUILD 文件里的一个 rule 调用,比如 swift_library(name = "foo") Label:Target 的全局 ID,形如 //foo/util:util Rule Rule 是构建函数,输入 sources + deps,输出 artifacts: ...

May 2, 2026

Swift 面试:泛型、协议与派发源码解析

Swift 面试:泛型、协议与派发源码解析 Swift 面试里,泛型和协议经常不是单独问语法,而是连着问:Swift 中函数派发机制有哪几种?泛型是编译期还是运行期机制?协议调用为什么可能慢?some 和 any 为什么性能不同?Protocol Witness Table 到底是什么? 这篇文章按面试题展开,把答案落到 Swift 编译器里的 Generic Signature、Generic Specializer、SIL 调用指令、Protocol Witness Table、class vtable 和 Devirtualize 优化。 面试高频问题 Swift 中函数派发机制有哪几种? 直接派发、class vtable 派发、witness table 派发、Objective-C 消息派发分别适用于什么场景? function_ref、class_method、witness_method、objc_method 在 SIL 里分别代表什么? 如何用 swiftc -emit-silgen / swiftc -emit-sil -O 观察派发指令? dynamic / @objc dynamic 会怎样影响派发? 如何用代码对比泛型、some、any 的派发和性能差异? Swift 泛型是运行时泛型还是编译期泛型? Generic Signature 是什么?它保存哪些信息? 泛型特化为什么能提升性能? Protocol Witness Table 是什么? 协议方法调用什么时候是静态派发,什么时候是动态派发? some Protocol 和 any Protocol 的本质区别是什么? final、private、具体类型为什么有利于优化? 协议扩展里的方法一定是动态派发吗? class_method 和 witness_method 有什么区别? 面试里如何解释“Swift 既强调协议,又强调性能”? 30 秒回答版 Swift 函数派发面试不要只答“静态派发和动态派发”。更完整的分类是:直接派发、class vtable 派发、protocol witness table 派发、Objective-C 消息派发,再补充优化器可能把动态调用去虚拟化成直接调用。 ...

June 16, 2026