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

APM-C端SDK架构

iOS APM SDK 是整个系统的数据入口。它运行在真实用户设备上,和业务 App 共进程、共资源、共生命周期,所以设计目标不是“能力越多越好”,而是 低开销、可控制、可降级、可追责、可合规。 一、SDK 职责边界 SDK 应该做: 采集 Crash、Watchdog、FOOM、卡顿、启动、网络、页面、MetricKit、业务 Trace 等现场。 生成并维护 session_id、view_id、action_id、resource_id、trace_id 等关联 ID。 做轻量预处理:去重、聚合、脱敏、采样、压缩、加密。 按数据价值分级落盘和上报。 接收远程配置,动态控制模块开关、采样率、阈值和熔断。 SDK 不应该做: 复杂 OLAP 查询。 大规模归因计算。 服务端符号化。 跨用户聚合。 长时间 CPU 采样或高频全量堆栈采样。 未经允许采集请求 body、用户输入、定位、通讯录等敏感数据。 二、分层架构 flowchart TB API["Public APIstart / identify / track / trace / breadcrumb"] Core["SDK Core生命周期 / 插件管理 / 远程配置 / 采样 / 隐私"] Context["Context ManagerApp / Device / User / Session / View / Trace"] Plugins["Plugin LayerCrash / Watchdog / FOOM / FPS / Launch / Network / MetricKit / Business"] Processor["Event Processor标准化 / 脱敏 / 聚合 / 去重 / 优先级"] Store["Local Storemmap / WAL / SQLite / 文件队列"] Uploader["Uploader批量 / 压缩 / 加密 / 重试 / 熔断"] API --> Core Core --> Context Core --> Plugins Plugins --> Processor Context --> Processor Processor --> Store Store --> Uploader Uploader --> Core 推荐模块: ...

May 7, 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

Block底层原理

基础概念 Block 是 Apple 在 C 语言基础上扩展的语法特性(也称为 Closure / 闭包),它允许将一段代码和其执行时需要的上下文环境封装为一个可传递的对象。Block 在 Objective-C 和 C/C++ 中均可使用,是 GCD、动画 API、回调等场景的核心机制。 // Block 的声明与调用 void (^myBlock)(NSString *) = ^(NSString *name) { NSLog(@"Hello, %@", name); }; myBlock(@"World"); 一个核心问题是:Block 是函数指针还是对象? 答案是——Block 本质上是一个 OC 对象。它的底层是一个包含 isa 指针的 C 结构体,满足 OC 对象的基本条件;同时它也包含一个函数指针(FuncPtr),指向 Block 实际执行的代码。因此可以说,Block 是一个用对象形式包装了函数指针和捕获变量的特殊 OC 对象。 底层数据结构 通过 clang -rewrite-objc 可以将 Block 语法转换为 C++ 代码,观察其底层结构。这个转换虽然不完全等同于最终编译产物,但准确反映了 Block 的数据布局和调用机制。 编译器转换示例 对以下源码: int main() { int a = 10; void (^block)(void) = ^{ NSLog(@"%d", a); }; block(); return 0; } Clang 会将其转换为如下结构: ...

May 2, 2026

MVI架构详解

什么是MVI MVI(Model-View-Intent)是一种单向数据流架构模式。“Model-View-Intent"这一命名最早出现在Andre Medeiros(Staltz)创建的JavaScript框架Cycle.js中。2016年,Hannes Dorfmann在其博客系列中将MVI系统化引入Android开发,他在文中明确提到同时受到了Cycle.js和Redux(以及更早的Elm架构)的启发——MVI的命名和响应式理念来自Cycle.js,而Reducer纯函数和单一状态源的机制则与Redux一脉相承。随着Swift社区对函数式编程和响应式编程的接受度提高,MVI也逐渐被引入iOS开发。 MVI的核心思想: 单向数据流:数据沿固定方向流动(View -> Intent -> Reducer -> State -> View),没有捷径或后门 不可变状态:整个界面由单一不可变的State描述,每次更新都产生全新的State对象 可预测性:给定相同的当前State和Intent,Reducer总是产生相同的新State MVI的核心概念 Model(状态) 在MVI中,Model不是传统意义上的领域数据模型,而是界面状态模型(State)。它用一个不可变的值类型来描述UI在某一时刻的完整快照,包括数据内容、加载状态、错误信息等。 struct UserListState: Equatable { var users: [User] var isLoading: Bool var error: String? var searchQuery: String static let initial = UserListState( users: [], isLoading: false, error: nil, searchQuery: "" ) } 这里使用var属性配合struct值类型,利用Swift的值语义在Reducer中通过拷贝实现不可变效果,这是Swift中最常用的做法(详见后文"状态的不可变性"一节)。 Intent(意图) Intent代表所有触发状态变化的事件。它不仅包括用户的UI操作,还包括副作用的结果回调(如网络请求完成、数据库查询结果)、系统事件(如生命周期回调、推送通知)等。所有这些事件统一通过Intent进入Reducer驱动状态变化。 enum UserListAction { // 用户意图 case loadUsers case refreshUsers case deleteUser(id: Int) case searchQueryChanged(String) // 副作用结果(也是Intent的一种) case usersLoaded([User]) case loadFailed(String) } 将用户操作和副作用结果统一为同一类型,是MVI的关键设计。这样Reducer就成为状态变化的唯一入口,所有状态转换都在一处完成。 ...

May 2, 2026

OOP、POP与AOP

在iOS开发中,经常会接触到三种重要的编程范式:面向对象编程(OOP)、面向协议编程(POP)和面向切面编程(AOP)。 OOP - 面向对象编程 基本概念 面向对象编程(Object-Oriented Programming)是一种以对象为核心的编程范式。它将数据和操作数据的方法封装在一起,形成对象。 OOP的四大核心特性: 特性 说明 iOS中的体现 封装 隐藏内部实现细节,只暴露必要的接口 @interface/@implementation分离,private属性 继承 子类继承父类的属性和方法 UIViewController继承自UIResponder 多态 同一接口可以有不同的实现 子类重写父类方法 抽象 提取共同特征形成抽象类型 抽象基类、协议 Objective-C中的OOP Objective-C是一门典型的面向对象语言,它在C语言的基础上添加了面向对象的特性。 // 基类定义 @interface Animal : NSObject @property (nonatomic, copy) NSString *name; - (void)speak; - (void)eat:(NSString *)food; @end @implementation Animal - (void)speak { NSLog(@"Animal speaks"); } - (void)eat:(NSString *)food { NSLog(@"%@ is eating %@", self.name, food); } @end // 子类继承 @interface Dog : Animal @property (nonatomic, copy) NSString *breed; @end @implementation Dog // 重写父类方法 - 多态 - (void)speak { NSLog(@"%@ barks: Woof!", self.name); } // 子类特有方法 - (void)fetch { NSLog(@"%@ is fetching", self.name); } @end Swift中的OOP Swift同样支持面向对象编程,但提供了更现代的语法和更强的类型安全。 ...

May 2, 2026

启动优化-Initializers

Initializers是指在main函数之前执行的初始化代码,包括C++静态构造函数和__attribute__((constructor))标记的函数。这些代码会在Pre-main阶段同步执行,阻塞启动。 问题分析 以下代码会在main之前执行: 1. C++静态构造函数 __attribute__((constructor)) static void MyInitializer() { // 耗时初始化 initializeHeavyResource(); } 2. 全局静态变量的构造函数 // 全局静态变量 static std::vector<std::string> globalCache = loadFromDisk(); // 构造函数在main前调用 3. 非基本类型的全局变量 // 非POD类型的全局变量会触发构造函数 static std::string globalString = "Hello"; static MyClass globalObject; 优化方案 方案1:延迟初始化 将全局变量改为懒加载模式: // 优化前:全局变量在main前初始化 static std::vector<std::string> globalCache = loadFromDisk(); // 优化后(纯C++方案,适用于 .cpp 文件): #include <mutex> static std::vector<std::string>* getGlobalCache() { static std::vector<std::string>* cache = nullptr; static std::once_flag onceFlag; std::call_once(onceFlag, [&] { cache = new std::vector<std::string>(); *cache = loadFromDisk(); }); return cache; } // 优化后(ObjC++方案,适用于 .mm 文件): static std::vector<std::string>* getGlobalCache() { static std::vector<std::string>* cache = nullptr; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ cache = new std::vector<std::string>(); *cache = loadFromDisk(); }); return cache; } 方案2:使用Swift懒加载 Swift的懒加载特性可以避免在启动时初始化: ...

May 2, 2026

编译优化-CocoaPods优化

在 iOS 生态中,CocoaPods 依然是大多数中大型工程的依赖管理工具。当 Pod 数量达到几百个子组件上千个时,pod install 的耗时会变成研发流程里的硬性卡点。抖音在其 seer-optimize 项目中系统化地优化了 CocoaPods 的整个生命周期,全量 Pod Install 耗时减少 50%、增量减少 65%。本文系统介绍这些优化背后的原理。 关于 CocoaPods 整体架构的深度介绍可以参考 CocoaPods源码导读-架构总览。 Pod Install 耗时构成 Installer#install! 的六个阶段: flowchart LR A[prepare] --> B[resolve_dependencies] B --> C[download_dependencies] C --> D[validate_targets] D --> E[generate_pods_project] E --> F[integrate_user_project] 抖音公开的典型耗时分布: 阶段 典型耗时 说明 prepare < 0.01s 初始化 resolve_dependencies 数秒到数分钟 依赖决议,瓶颈 download_dependencies 数秒到数分钟 依赖下载,IO 密集 validate_targets 0.1s 校验 generate_pods_project 数秒到数十秒 生成 xcodeproj integrate_user_project 0.01s 集成主工程 resolve_dependencies 优化 Source 仓库更新 默认 CocoaPods 在 pod install --repo-update 时会更新所有 source 仓库。抖音改造为: ...

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

Codable底层原理

Codable 是 Swift 4 引入的序列化方案,官方定位是替代 Objective-C 时代的 NSCoding、以及各种第三方字典转模型框架(MJExtension、YYModel、JSONModel 等)。与运行时反射方案不同,Codable 通过"编译器自动合成 + 标准库协议抽象 + 具体格式实现"三层解耦,在编译期就把类型与序列化逻辑绑定好,兼具类型安全、性能和扩展性。 本文基于以下 Swift 源码展开分析: 协议定义:swift/stdlib/public/core/Codable.swift 编译器合成:swift/lib/Sema/DerivedConformanceCodable.cpp、DerivedConformanceCodingKey.cpp Foundation 实现:swift-corelibs-foundation/Sources/Foundation/JSONEncoder.swift、JSONDecoder.swift、PropertyListEncoder.swift 整体架构 在开始深入之前,先建立整体印象。Codable 可以拆成三层角色: 用户类型层:业务中的 struct / class / enum,声明遵循 Codable 标准库与编译器层:标准库定义 Encodable / Decodable / Encoder / Decoder 等协议,编译器为符合条件的类型合成 encode(to:) 和 init(from:) 格式实现层:Foundation 或第三方提供具体 Encoder / Decoder,负责把容器操作落到 JSON、Plist、BSON 等格式上 flowchart LR subgraph L1[用户类型层] A[struct / class / enum声明遵循 Codable] end subgraph L2[标准库与编译器层] B[Encodable / Decodable协议要求] C["编译器合成encode(to:) / init(from:)"] D[Encoder / Decoder容器协议] B --> C C --> D end subgraph L3[格式实现层] E[JSONEncoder / JSONDecoderPropertyListEncoder / 第三方实现] F[Data / 字符串 / 其他格式] E --> F end A -->|conforms to| B D -->|由具体类型实现| E 核心思想是:标准库只定义"如何描述一个可编码类型"和"编码器需要提供什么能力",具体的格式(JSON、Plist、BSON…)由 Foundation 或第三方实现。用户类型只需要遵循协议,剩下的由编译器在 SIL 层自动生成。 ...

June 8, 2026