Texture源码导读

TODO: 待补充

May 2, 2026

Objective-C底层原理 - NSObject

本文将深入探讨Objective-C中所有对象的基类NSObject的底层实现原理,涵盖对象本质、isa指针机制、Tagged Pointer、引用计数、内存布局等核心概念。 NSObject 的本质:C 结构体 在Objective-C的底层实现中,NSObject的本质是一个C结构体。在苹果运行时源码中可以看到: // 底层运行时定义 struct objc_object { Class isa; // 指向对象所属类的指针 }; // 类型别名定义 typedef struct objc_object NSObject; 这意味着,当我们通过NSObject *obj = [[NSObject alloc] init];创建一个 NSObject 实例时,实际上在堆上分配了一个struct objc_object结构体,而变量obj只是一个指向该结构体的指针。 // 这行代码在底层实际上做了类似下面的事情: NSObject *obj = [[NSObject alloc] init]; // 等效的伪C代码: // 1. 调用 alloc 类方法,其核心是调用 malloc 在堆上分配一块足够大的内存 // 大小至少是 sizeof(struct objc_object),即一个isa指针的大小 NSObject *obj = (NSObject *)malloc(sizeof(struct objc_object)); // 2. 初始化这块内存,最重要的是设置 isa 指针,让它指向 NSObject 这个类 obj->isa = [NSObject class]; // 实际过程更复杂,但本质如此 // 3. 其他初始化工作 (init) 那么这里提到的isa指针是什么呢? ...

May 2, 2026

编译优化-头文件与HMap

对以 Objective-C 为主或混编的 iOS 大型工程,头文件查找是一个被严重低估的编译开销点。美团的统计显示,400+ Pod 组件的工程会产生近 5 万个头文件,导致海量的 IO 操作和编译参数膨胀。Header Map(HMap)技术能把头文件查找从 O(n) 的目录扫描退化为 O(1) 的哈希查表。 头文件查找的代价 Clang 的查找流程 当 Clang 遇到 #import <AFNetworking/AFNetworking.h> 时: flowchart TD A[遇到 #import] --> B{是否系统头?} B -- 是 --> C[SYSTEM_HEADER_SEARCH_PATHS] B -- 否 --> D[USER_HEADER_SEARCH_PATHS] C --> E[按顺序遍历 HEADER_SEARCH_PATHS] D --> E E --> F{路径下有吗?} F -- 否 --> G[下一个路径] G --> E F -- 是 --> H[stat + open] H --> I[解析头文件] 每一次查找都要对所有 HEADER_SEARCH_PATHS 执行 stat(2) 系统调用,当路径数量达到数千时,光 stat 就是显著开销。 ...

May 2, 2026

微服务化架构详解

什么是客户端微服务化 微服务化是将后端微服务的思想应用到客户端,将App内部按业务域拆分为多个独立服务。每个服务拥有独立的数据存储和业务逻辑,服务间通过定义良好的接口通信。 微服务化的核心关注点是: 业务域的逻辑划分:按领域边界划分服务,而非按代码结构 运行时隔离:服务在运行时保持独立,数据不互相污染 服务自治:每个服务独立演进,对外提供稳定的契约 客户端微服务化 vs 后端微服务 维度 后端微服务 客户端微服务化 部署 独立部署、独立运行的进程 同一App内的独立服务 通信 HTTP/RPC/消息队列 进程内通信(协议、事件总线) 数据库 每个服务独立数据库 每个服务独立数据存储空间 扩缩容 水平扩展多实例 不适用 故障隔离 进程级隔离 逻辑隔离(防御性编程) 核心目标 独立部署、水平扩展 业务域划分、运行时隔离 微服务化与组件化的关系 微服务化和组件化是不同维度的概念: flowchart TB subgraph 组件化视角["组件化视角 (物理结构)"] C1["首页组件"] C2["商城组件"] C3["用户组件"] C4["订单组件"] end subgraph 微服务化视角["微服务化视角 (逻辑划分)"] S1["用户服务"] S2["交易服务"] S3["内容服务"] end C3 -.->|"实现"| S1 C4 -.->|"实现"| S2 C2 -.->|"实现"| S2 C1 -.->|"实现"| S3 维度 组件化 微服务化 关注点 代码的物理隔离、编译解耦 业务域的逻辑划分、运行时隔离 划分依据 功能模块、代码边界 业务领域、数据边界 解决的问题 编译依赖、团队协作、代码复用 业务自治、数据隔离、服务演进 粒度 可大可小(页面、功能模块) 通常较粗(业务域) 实践中两者结合使用: ...

May 2, 2026

启动优化-观测

准确测量启动时间是优化的前提。启动时间是用户体验的关键指标之一,研究表明,应用性能直接影响用户留存:页面加载时间每增加1秒,用户流失率会显著上升。对于iOS应用,需要在20秒内完成启动,否则可能会被watchdog强制终止。 本文介绍如何测量和监控iOS应用的启动时间。关于启动流程的详细介绍,请参考 App启动流程。 一、测量方式概览 iOS 提供了多种测量启动时间的方式,适用于不同场景: 方式 适用场景 粒度 是否需要代码 dyld 环境变量 开发调试 定性分析 否 Instruments App Launch 深度分析 函数级 否 代码埋点 开发调试 + 线上监控 自定义 是 MetricKit 线上监控 冷启动/热启动 少量代码 二、开发调试阶段 2.1 dyld 环境变量 通过设置 dyld 环境变量可以在控制台查看启动过程中的加载信息: Edit Scheme → Run → Arguments → Environment Variables 与启动测量相关的环境变量: 环境变量 作用 启动分析用途 DYLD_PRINT_LIBRARIES 打印每个加载的 mach-o 镜像 查看动态库加载顺序,确认是否加载了不必要的库 DYLD_PRINT_LOADERS 打印镜像的加载器类型(JustInTimeLoader / PrebuiltLoader) 分析是否使用了启动闭包优化 DYLD_PRINT_INITIALIZERS 打印每个 initializer 的执行 查看 +load、constructor、C++ 静态构造函数的执行顺序 DYLD_PRINT_BINDINGS 打印每次符号绑定 分析符号绑定情况(输出量很大) DYLD_PRINT_SEARCHING 打印库搜索路径 排查库加载问题 DYLD_PRINT_APIS 打印 dyld API 调用(如 dlopen) 检测运行时动态加载行为 DYLD_PRINT_TO_FILE 将日志输出到指定文件 避免控制台日志过多,方便后续分析 使用示例: ...

May 2, 2026

Swinject源码导读

Swinject 是一款轻量级的 Swift 依赖注入(Dependency Injection)框架,灵感来自 .NET 的 Ninject。它通过类型安全的方式管理对象之间的依赖关系,将对象的创建和使用解耦。本文基于 v2.8.3 源码进行分析。 一、整体架构 Swinject 的核心设计围绕**注册-解析(Register-Resolve)**模式展开,整个框架仅约 20 个源码文件,代码极为精简。 graph TB subgraph "组织层" A["Assembler"] B["Assembly (Protocol)"] end subgraph "核心层" C["Container"] D["Resolver (Protocol)"] end subgraph "注册层" E["ServiceEntry<Service>"] F["ServiceKey"] end subgraph "作用域层" G["ObjectScope"] H["InstanceStorage (Protocol)"] end subgraph "存储实现" I["TransientStorage"] J["GraphStorage"] K["PermanentStorage"] L["WeakStorage"] end subgraph "包装器" M["Lazy<Service>"] N["Provider<Service>"] end A -->|"管理"| B B -->|"assemble(container:)"| C C -->|"实现"| D C -->|"存储注册信息"| E E -->|"由 ServiceKey 索引"| F E -->|"关联"| G G -->|"创建"| H H --- I H --- J H --- K H --- L C -->|"延迟解析"| M C -->|"每次新建"| N 源码文件结构: ...

May 2, 2026

启动优化-减少动态库

动态库加载是Pre-main阶段的重要组成部分,每个动态库都需要加载、验证签名、进行符号绑定,这些操作会显著影响启动时间。 问题分析 当App启动时,dyld需要: 分析Mach-O文件的Load Commands,找出依赖的动态库 递归加载所有依赖的动态库 对每个动态库进行签名验证 执行Rebase和Bind操作 动态库数量越多,这些操作的耗时就越长。Apple建议将自定义动态库数量控制在6个以内。关于动态库加载的详细流程,可以参考Mach-O的链接、装载与库。 关于缓存机制: 系统动态库:已被放入 dyld shared cache 中,其加载和符号绑定操作已预先完成,不会影响 App 启动时间 App 动态库:dyld 3 引入的 Launch Closure 机制会缓存依赖分析、Rebase/Bind 信息等元数据。首次启动(或 App 更新后)会生成缓存,后续启动直接使用 但即使有 Launch Closure 缓存,Rebase/Bind 操作本身仍需执行(因为 ASLR slide 每次启动都不同),动态库数量越多,这些操作的耗时仍然越长。 App可执行文件 ├── UIKit.framework │ ├── Foundation.framework │ │ └── CoreFoundation.framework │ └── CoreGraphics.framework ├── 自定义Framework A │ └── 依赖库... └── 自定义Framework B └── 依赖库... 优化方案 1. 合并动态库 将功能相近的动态库合并为一个: ...

May 2, 2026

SnapKit源码导读

SnapKit 是 iOS / macOS 社区最广泛使用的 Auto Layout DSL 库,由 Robert Payne 等人维护,目前在 GitHub 已收获 20k+ star。它在 NSLayoutConstraint 之上构建了一整套链式、类型安全、面向协议的声明式约束语法。本文基于 v5.7.1(2025 年 2 月发布,最后 master commit 2025-05-08 19f59a6)源码进行分析,代码总量不足 3500 行,却用一个分级的 Builder 链路,把 NSLayoutConstraint 那套冗长的 API 封装成了今天我们习惯的 make.left.equalTo(x).offset(10)。 一、整体架构 SnapKit 的核心设计可以用一句话概括:用编译期强约束的链式 Builder,驱动一个延迟构建的 ConstraintDescription,最终在闭包结束后一次性生成 NSLayoutConstraint 并激活。 graph TB subgraph "DSL 入口层" V["UIView / NSView / UILayoutGuide"] SNP["view.snp(ConstraintViewDSL)"] MK["makeConstraintsremakeConstraintsupdateConstraints"] end subgraph "Builder 链(阶段化构建器)" CM["ConstraintMaker(属性起点 make.left)"] CME["ConstraintMakerExtendable(追加属性 .top.right)"] CMR["ConstraintMakerRelatable(关系 equalTo/greaterThan)"] CMED["ConstraintMakerEditable(multipliedBy/offset/inset)"] CMP["ConstraintMakerPrioritizable(priority)"] CMF["ConstraintMakerFinalizable(labeled)"] end subgraph "描述 & 协议层" DESC["ConstraintDescription(懒加载装配)"] TARGETS["Target 协议族Relatable/Constant/OffsetInset/Multiplier/Priority"] ATTR["ConstraintAttributes(OptionSet 位运算)"] end subgraph "约束层" C["Constraint(封装一组 LayoutConstraint)"] LC["LayoutConstraint(NSLayoutConstraint 子类)"] ITEM["LayoutConstraintItem(UIView/UILayoutGuide)"] end subgraph "系统层" NSLC["NSLayoutConstraint.activate"] end V -->|扩展属性| SNP SNP --> MK MK -->|创建| CM CM -->|返回| CME CME -->|继承| CMR CMR -->|equalTo| CMED CMED -->|继承| CMP CMP -->|priority| CMF CM -.写入.-> DESC CME & CMR & CMED & CMP & CMF -.读写.-> DESC DESC -.lazy 构建.-> C C --> LC C -.关联对象.-> ITEM DESC -.使用.-> ATTR CMR -.类型匹配.-> TARGETS C --> NSLC 源码目录(Sources/,约 3500 行) Sources/ ├── ConstraintView.swift # typealias UIView/NSView → ConstraintView ├── ConstraintLayoutGuide.swift # typealias UILayoutGuide/NSLayoutGuide ├── ConstraintLayoutSupport.swift # typealias UILayoutSupport ├── Typealiases.swift # LayoutRelation / LayoutAttribute / LayoutPriority ├── ConstraintConfig.swift # interfaceLayoutDirection 全局开关 │ ├── ConstraintView+Extensions.swift # view.snp 入口(含 snp_ 老别名) ├── ConstraintLayoutGuide+Extensions.swift # guide.snp 入口 ├── UILayoutSupport+Extensions.swift # topLayoutGuide.snp(iOS 11 前) ├── ConstraintViewDSL.swift # makeConstraints / remake / update / remove ├── ConstraintLayoutGuideDSL.swift # LayoutGuide 版 DSL ├── ConstraintLayoutSupportDSL.swift # LayoutSupport 版 DSL ├── ConstraintDSL.swift # DSL 协议 + 属性定义(left/top/edges/...) │ ├── ConstraintMaker.swift # 属性起点(make.left / make.edges / ...) ├── ConstraintMakerExtendable.swift # 可追加属性(.top.right) ├── ConstraintMakerRelatable.swift # equalTo / lessThanOrEqual / greaterThanOrEqual ├── ConstraintMakerRelatable+Extensions.swift # equalToSuperview 闭包版 ├── ConstraintMakerEditable.swift # multipliedBy / dividedBy / offset / inset ├── ConstraintMakerPrioritizable.swift # priority(...) ├── ConstraintMakerFinalizable.swift # labeled(...) + .constraint │ ├── ConstraintAttributes.swift # OptionSet 属性位图 ├── ConstraintRelation.swift # equal / lessThanOrEqual / greaterThanOrEqual ├── ConstraintPriority.swift # required/high/medium/low ├── ConstraintDescription.swift # Builder 中间态 + lazy Constraint ├── ConstraintItem.swift # (target, attributes) 二元组(弱引用) ├── LayoutConstraintItem.swift # UIView/UILayoutGuide 统一协议 ├── ConstraintInsets.swift # UIEdgeInsets typealias ├── ConstraintDirectionalInsets.swift # NSDirectionalEdgeInsets(iOS 11+) │ ├── ConstraintRelatableTarget.swift # 可作为 equalTo 参数的类型标记 ├── ConstraintConstantTarget.swift # constant 目标 + 属性→CGFloat 的派发 ├── ConstraintOffsetTarget.swift # offset 目标 ├── ConstraintInsetTarget.swift # inset 目标(含标量→insets 转换) ├── ConstraintDirectionalInsetTarget.swift # 方向 insets 目标 ├── ConstraintMultiplierTarget.swift # multiplier 目标 ├── ConstraintPriorityTarget.swift # priority 目标(Int/Float/UILayoutPriority) │ ├── Constraint.swift # 真正的约束对象 + activate/deactivate ├── LayoutConstraint.swift # NSLayoutConstraint 子类,回指 Constraint ├── Debugging.swift # LayoutConstraint.description 美化 └── PrivacyInfo.xcprivacy # 隐私清单 核心设计思想 模式 作用 源码体现 阶段化 Builder(Staged Builder / Step Builder) 通过继承链控制链式调用的合法顺序,让 make.left.offset(10).equalTo(...) 这类非法组合在编译期报错 ConstraintMaker → Extendable → Relatable → Editable → Prioritizable → Finalizable 协议 + 类型擦除(POP) 让 Int / Float / CGFloat / CGSize / CGPoint / UIEdgeInsets / UIView / ConstraintItem 等异构类型可以统一作为 equalTo / offset / priority 的参数 Constraint*Target 协议族 OptionSet 位运算 用 32 位整型表达「属性集合」,.edges = [.left, .top, .right, .bottom] 这类聚合用位或实现,避免反复建数组 ConstraintAttributes Associated Object 在不污染 UIView 原类的前提下,为每个 View 绑定「它持有的 SnapKit Constraint 集合」和「snp.label」 LayoutConstraintItem.constraintsSet + ConstraintDSL.setLabel Lazy 求值 ConstraintDescription.constraint 是 lazy var,闭包执行结束前只是收集参数,真正的 Constraint 构造和 NSLayoutConstraint 生成都推迟到最后 ConstraintDescription.constraint 跨平台 typealias 通过一组 typealias 把 iOS / macOS / tvOS 的 UIView/NSView、UIEdgeInsets/NSEdgeInsets、UILayoutPriority/NSLayoutConstraint.Priority 等抹平 ConstraintView.swift / Typealiases.swift 二、DSL 入口:view.snp SnapKit 没有像 Masonry 那样污染 UIView 的命名空间,而是通过一个命名空间结构体暴露 DSL。 ...

May 2, 2026

iOS音视频原理

音视频开发是iOS中一个庞大且核心的技术领域,涵盖采集、编码、传输、解码、渲染等完整链路。本文从基础概念出发,深入iOS平台提供的音视频框架和底层原理。 音视频处理全链路 一个完整的音视频系统(如直播、视频通话、短视频)包含以下环节: flowchart LR A["采集Camera/Mic"] --> B["前处理美颜/降噪"] B --> C["编码H.264/AAC"] C --> D["封装FLV/MP4"] D --> E["传输RTMP/HLS"] E --> F["解封装解析容器"] F --> G["解码VideoToolbox"] G --> H["后处理滤镜/混音"] H --> I["渲染Metal/Speaker"] 环节 做什么 iOS中对应的技术 采集 从摄像头/麦克风获取原始数据 AVCaptureSession 前处理 美颜、滤镜、降噪、回声消除 CoreImage、vDSP 编码 将原始数据压缩为体积更小的格式 VideoToolbox、AudioToolbox 封装 将编码后的音视频数据打包到容器中 AVAssetWriter 传输 通过网络发送到服务器或其他端 URLSession、第三方推流SDK 解封装 从容器中分离出音频流和视频流 AVAssetReader 解码 将压缩数据还原为原始像素/采样数据 VideoToolbox、AudioToolbox 后处理 滤镜、水印、混音等 CoreImage、Metal 渲染 将画面显示到屏幕、声音输出到扬声器 AVSampleBufferDisplayLayer、Metal、AudioUnit 核心术语 在深入细节之前,先了解几个贯穿全文的核心概念: 什么是编解码器(Codec) Codec = Coder + Decoder,即编码器+解码器的合称。编码器负责将原始音视频数据压缩为更小的格式,解码器负责将压缩数据还原。 音视频中有两类完全不同的"格式"概念,初学者极易混淆: 编码格式(Codec):定义数据怎么压缩 视频:H.264、H.265、VP9、AV1 音频:AAC、MP3、Opus、FLAC 封装格式(Container):定义压缩后的数据怎么组织存储 MP4、MOV、FLV、MKV、TS 两者是独立的,可以自由组合: MP4容器 + H.264视频 + AAC音频 (最常见的组合) MOV容器 + H.265视频 + AAC音频 (iPhone录制默认) FLV容器 + H.264视频 + AAC音频 (直播推流常用) MKV容器 + AV1视频 + Opus音频 (新一代免费组合) 什么是H.264和H.265 H.264(也叫AVC,Advanced Video Coding)是目前使用最广泛的视频编码标准,由ITU-T和ISO/IEC联合制定,2003年发布。它定义了一套将视频帧压缩为比特流的算法规范——包括如何划分图像块、如何预测、如何变换量化等。几乎所有设备和平台都支持H.264硬件解码。 ...

May 2, 2026

APM-数据采集

本文聚焦 iOS APM SDK 的采集层,把 APM-指标体系 定义好的指标落地到具体代码与底层原理。按照采集对象分八个模块展开:崩溃、卡死、内存/OOM、FPS 卡顿、启动、网络、CPU/磁盘、业务。 原理深度与相关文章联动:本文给出整体采集设计与关键代码;单项(如 Mach 异常、Signal、MemoryGraph 等)的纯原理细节在"崩溃/卡顿/启动/耗电"系列中已有深入剖析,本篇通过链接形式串联,避免重复。 一、采集技术图谱 graph TB subgraph Hook[Hook 技术] H1[Method SwizzlingObjective-C Runtime] H2[fishhookC 函数符号重绑] H3[Objc Categroy + +load] H4[KVO / NSNotificationCenter] H5[C++ Virtual Table Hook] end subgraph System[系统 API] S1[MetricKit] S2[os_signpost / OSLog] S3[Runtimetask_* / thread_*] S4[libdispatch signpost] S5[URLSessionTaskMetrics] end subgraph Signal[信号/异常] E1[NSSetUncaughtExceptionHandler] E2[signal / sigaction] E3[Mach Exception Port] E4[libc++ abi 异常] end subgraph Sampling[采样] P1[RunLoop Observer] P2[Timer/GCD Source] P3[CADisplayLink] P4[backtrace 回溯] end 二、崩溃采集 原理文章:崩溃-原理 · 崩溃-Mach异常 · 崩溃-信号处理 · 崩溃-采集 ...

May 2, 2026