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

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

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

Objective-C 消息发送 objc_msgSend 与慢速查找入口

objc4 runtime dispatch Objective-C 消息发送:objc_msgSend 与慢速查找入口 Objective-C 的方法调用本质上是一次以 receiver 和 SEL 为键的动态派发。 在 arm64 上,objc_msgSend 先用少量汇编完成 nil/tagged pointer 判断和方法缓存查找; 只有缓存未命中时,才进入 C++ 慢路径 lookUpImpOrForward,沿类层级查找、触发动态解析、填充缓存或进入消息转发。 入口:runtime/Messengers.subproj/objc-msg-arm64.s 慢路径:runtime/objc-runtime-new.mm 缓存:runtime/objc-cache.mm 测试:test/msgSend.m 目录 作用 实现原理总览 核心结构与约定 快速路径 慢速查找入口 消息转发 测试观察点 作用 objc_msgSend(id self, SEL _cmd, ...) 是 Objective-C 实例方法和类方法调用的公共派发入口。 编译器把 [obj method:arg] 形式的调用降低为对 objc_msgSend 的调用;真正执行哪个 IMP 由运行时根据接收者的实际类、选择子和当前方法缓存决定。 动态派发 同一个 SEL 可以在不同类上命中不同 IMP,体现多态。 缓存加速 大多数热路径只扫描类的 cache_t,命中后直接尾调用 IMP。 语义兼容 nil 接收者返回零值;tagged pointer 通过标签映射到伪类继续派发。 扩展入口 未找到实现时支持 +resolve... 和完整的 forwarding 机制。 ...

June 1, 2026

Alamofire源码导读

Alamofire 是 Swift 社区最广泛使用的 HTTP 网络库,由 Alamofire Software Foundation 维护,在 GitHub 已收获 42k+ star。它在 URLSession 之上构建了一整套链式 API、拦截器、认证、证书校验、重试与响应序列化等能力。本文基于最新版本 v5.11.2(2026 年 4 月发布)源码进行分析,覆盖 Swift 6 严格并发、async/await、WebSocket、OfflineRetrier 等最新特性。 一、整体架构 Alamofire 采用中央调度 + 状态机 + 协议导向的设计,核心角色分工清晰: graph TB subgraph "入口层" AF["AF(Session.default)"] end subgraph "调度层" S["Session统一调度器"] SD["SessionDelegateURLSession 桥接"] end subgraph "请求层" R["Request (基类)状态机 + 生命周期"] DR["DataRequest"] DLR["DownloadRequest"] UR["UploadRequest"] DSR["DataStreamRequest"] WSR["WebSocketRequest"] end subgraph "拦截层" RA["RequestAdapter请求改写"] RR["RequestRetrier失败重试"] RI["RequestInterceptor= Adapter + Retrier"] end subgraph "服务层" RS["ResponseSerializer响应序列化"] STE["ServerTrustEvaluating证书/公钥校验"] EM["EventMonitor事件监控"] MP["MultipartFormData表单编码"] end subgraph "系统层" US["URLSession / URLSessionTask"] end AF --> S S --> SD S -->|创建| R R --> DR & DLR & UR & DSR & WSR S -.使用.-> RI RI -.= .-> RA & RR R -.序列化.-> RS SD -.校验.-> STE S -.通知.-> EM UR -.构建.-> MP SD <-->|delegate| US R -->|执行| US 源码目录(Source/,总计约 17000 行纯 Swift 代码): ...

May 2, 2026

Swift 面试:值语义、COW 与集合源码解析

Swift 面试:值语义、COW 与集合源码解析 Swift 的 Array、String、Dictionary 都表现为值类型,但它们不可能每次赋值都完整复制底层存储。面试官问值语义和 COW,真正想看的是你能不能说清楚:值语义是语言语义,COW 是性能实现;集合表面是 struct,底层经常共享引用存储;写入前靠唯一性检查决定是否复制。 这篇文章从面试题出发,把结论落到 Swift 标准库源码里的 Array buffer、String guts、Dictionary storage 和 isKnownUniquelyReferenced。 面试高频问题 Swift 的 struct 为什么常说是值语义? Array 赋值时一定会复制底层元素吗? Copy-on-Write 的触发条件是什么? isKnownUniquelyReferenced 检查的到底是什么? Array、ContiguousArray、ArrayBuffer、Storage 是什么关系? String 为什么不能用整数下标随机访问? String.Index 为什么不是一个简单的 Int? Dictionary 也是 COW 吗? ArraySlice 为什么可能持有原数组存储? 值类型里包含 class 引用,还算值语义吗? 30 秒回答版 Swift 的值语义是说:从语言使用者角度看,赋值、传参、修改不会意外影响另一个值。但这不等于底层每次都立即复制。 标准库集合通常用 Copy-on-Write 实现:多个值可以共享同一份堆上 buffer;只有当某个值要写入时,才检查底层 buffer 是否唯一引用。如果唯一,就原地修改;如果不唯一,就复制一份新 buffer 再修改。 所以: 值语义:用户看到的是独立值 COW:实现上先共享,写入前再判断是否复制 ARC:维护底层 buffer 的引用计数 isUnique:让 COW 判断能否原地修改 面试可以这样回答: Swift 的 Array 是 struct,但它内部持有引用语义的 storage。赋值时通常只复制结构体里的引用;真正写入时通过唯一性检查决定是否复制底层 storage。这让 Array 同时具备值语义和接近引用共享的性能。 ...

June 16, 2026

objc4 方法缓存 cache_t / bucket_t 技术讲解

objc4 方法缓存 cache_t / bucket_t 技术讲解 本文基于当前仓库中的 runtime/objc-cache.mm、 runtime/objc-runtime-new.h、runtime/objc-runtime-new.mm 以及 cache flush 相关测试,说明 Objective-C runtime 如何把一次慢速方法查找变成后续的快速 objc_msgSend cache hit。 一、方法缓存的作用 Objective-C 消息发送以 (Class, SEL) 为核心输入。完整方法查找需要处理类实现、父类链、 动态方法解析、转发和分类变更等逻辑,代价高于一次普通函数调用。cache_t 是挂在每个 objc_class 上的 IMP cache,用 SEL 作为 key,缓存最终应该调用的 IMP。 加速热路径命中时 objc_msgSend 不进入 runtime 慢路径,直接由 bucket 中的 IMP 跳转。 缓存继承结果子类未实现某 selector 时,也可以把父类找到的 IMP 缓存在子类 cache 中。 支持动态变更分类加载、添加方法、交换实现等操作会触发 cache flush,避免继续调用旧 IMP。 cache 不是方法列表本身,也不是权威数据源。它是一个可丢弃、可重建的性能结构;扩容时甚至不会搬迁旧条目, 而是让后续消息重新填充热点 selector。 二、核心结构 bucket_t:一个 selector 到 IMP 的槽位 字段 _sel 保存 selector,_imp 保存编码后的 IMP。arm64 上 IMP 在前,其他架构通常 SEL 在前,以贴合汇编 fast path 和指针认证需求。 sel() 以 relaxed atomic 读取 selector。空槽的 selector 为 0。 imp(base, cls) 读取并解码 IMP。arm64e 可使用 ptrauth,部分配置使用 class 指针 XOR,未编码配置则直接返回。 set<Atomic, Encoded>() 写入一个 bucket。写入顺序被精心安排,保证无锁读取者不会看到“新 SEL + 旧 IMP”的错误组合。 cache_t:每个类上的哈希表 _bucketsAndMaybeMask 保存 buckets 指针,有些 64 位配置还把 mask 打包进高位;preoptimized cache 也用低位 marker 复用这个字段。 _mask / 内联 mask bucket 数量始终为 2 的幂,mask = capacity - 1,哈希后用按位与得到起始槽。 _occupied 已占用动态 bucket 数。插入成功后递增;换表时清零。 _flags 保存若干快速路径标记,例如 metaclass、C++ ctor/dtor、默认 alloc 或 RR 等,具体位定义在 objc-runtime-new.h。 关键方法 insert、eraseNolock、destroy、copyCacheNolock、maybeConvertToPreoptimized、preoptFallbackClass。 preopt_cache_t:dyld shared cache 预构建的常量 cache fallback_class_offset 预优化查找未覆盖时继续查找的类,相对当前 class 地址保存。 shift / mask 用于计算预优化 entries 下标。capacity() 返回 mask + 1。 occupied 预优化 cache 中的有效条目数量。 has_inlines 标记是否存在内联 selector;方法列表变更时需要更谨慎地禁用这类 cache。 entries[] 每项保存 selector offset 和 IMP offset,而不是直接保存完整指针。 三、实现原理 动态 cache 是一个开放寻址哈希表。cache_hash(sel, mask) 用 selector 地址和 mask 得到起始槽; 冲突时调用 cache_next 继续探测。不同架构的探测方向不同:部分架构递增并使用 end marker, arm64 递减并通过 mask 回绕。 ...

June 1, 2026

IGListKit源码导读

IGListKit 是 Instagram(Meta)开源的一款数据驱动的 UICollectionView 框架,旨在构建快速、灵活的列表。它的核心思想是将每个数据对象映射为独立的 Section Controller,通过高效的 O(n) Diff 算法自动计算数据变化并应用最小化更新,避免手动调用 performBatchUpdates 或 reloadData。本文基于 v5.2.0 源码(2026年2月发布)进行分析。 一、整体架构 IGListKit 采用分层架构,核心由三大模块构成: graph TB subgraph "使用层" A["UIViewController"] end subgraph "适配层" B["IGListAdapter"] C["IGListAdapterDataSource"] end subgraph "控制层" D["IGListSectionController"] E["IGListBindingSectionController"] end subgraph "更新层" F["IGListAdapterUpdater"] G["IGListUpdateCoalescer"] H["IGListUpdateTransaction"] end subgraph "Diff层 (IGListDiffKit)" I["IGListDiffPaul Heckel算法"] J["IGListDiffable协议"] end subgraph "视图层" K["UICollectionView"] end A --> B B --> C B --> D D --> E B --> F F --> G F --> H H --> I I --> J B --> K 源码目录结构: ...

May 2, 2026

Swift 面试:并发、async/await 与 Actor 源码解析

Swift 面试:并发、async/await 与 Actor 源码解析 Swift Concurrency 面试经常从 async/await 问起,但真正的重点是:Task 是结构化并发的执行单元,await 是显式暂停点,Actor 通过隔离和串行执行器保护状态,Sendable 则约束跨并发边界传递的数据。 这篇文章按面试题展开,把结论落到 Swift 标准库和编译器源码里的 Actor、GlobalActor、MainActor、AsyncLet、TaskGroup、Sendable 和 ActorIsolation。 面试高频问题 async/await 和 GCD 的关系是什么? await 到底表示线程阻塞还是任务暂停? Task、async let、TaskGroup 有什么区别? Actor 如何保证数据隔离? MainActor 是什么,为什么 UI 更新要回到 MainActor? GlobalActor 和普通 Actor 有什么区别? Sendable 解决什么问题? Task.detached 为什么要慎用? Actor 之间调用为什么需要 await? Swift Concurrency 是编译器机制还是运行时机制? 30 秒回答版 Swift Concurrency 是编译器、标准库和运行时共同完成的并发模型。 async/await 不是对 GCD 的简单语法糖。await 表示当前异步任务可能在这里暂停,把执行权交还给调度器;等异步结果可用时,再从 continuation 恢复。它不应该理解成“阻塞当前线程等待”。 Actor 是一种受隔离保护的引用类型。Actor 内部可变状态只能在 actor 隔离域内访问;跨 actor 访问必须异步,编译器会插入隔离检查。运行时通过 executor 保证同一 actor 的隔离状态不会被多个任务同时进入。 ...

June 16, 2026