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

Tagged Pointer 对象实现讲解

objc4 runtime internals Tagged Pointer 对象 Tagged Pointer 是一种“指针即对象”的表示法:对象没有堆内存,类信息和 payload 直接编码在指针位中。objc4 对它提供了创建、取 tag、取 payload、取类、消息发送、禁用和混淆等完整路径。 目录 作用 实现原理 核心宏、结构、函数 关键流程 测试视角 源码依据 作用 减少堆分配 小整数、短字符串、日期、索引路径等小对象可以直接塞进指针。NSNumber 的小整数就是典型例子,test/taggedNSPointers.m 验证了 NSNumber numberWithInt:1234 是 tagged pointer。 保留对象语义 虽然指针不指向对象内存,它仍能接收消息、查询类、作为字典 key/value,并通过 Foundation/CF 桥接。运行时把 tag 映射到真实 Class 后,继续走常规方法缓存查找。 绕开生命周期表 test/taggedPointers.m 验证 retain、release、autorelease、weak store/load 对 tagged pointer 不产生真实引用计数或弱引用表负担。它们本质上是立即值,不需要释放。 **一句话:**Tagged Pointer 把“对象身份”改成“位编码值”。运行时需要做的,是在所有需要对象元数据的地方识别它,并把 tag 翻译成类。 实现原理 objc4 在 64 位平台启用 tagged pointer。runtime/objc-config.h 中 SUPPORT_TAGGED_POINTERS 对 __LP64__ 为 1;非 64 位平台关闭。tag 标记位在大多数平台使用 MSB,macOS x86_64 使用 LSB。arm64 还使用 split tagged pointer 布局。 ...

June 1, 2026

Texture源码导读

TODO: 待补充

May 2, 2026

objc4:isa_t、nonpointer isa、引用计数与 SideTable

isa_t、nonpointer isa、引用计数与 SideTable 本文基于 objc4 仓库中的 runtime 源码和相关测试,解释 Objective-C 对象头里的 isa_t 如何同时承载类信息、引用计数、弱引用/关联对象标记,以及这些信息何时转移到 SideTable。 作用 实现原理与位布局 核心结构和关键字段方法 关键流程 测试如何约束行为 1. 作用 传统 Objective-C 对象的第一个机器字是 isa,直接保存类指针。objc4 在支持 SUPPORT_NONPOINTER_ISA 的平台上把这个机器字扩展为 isa_t: 低位和高位保存状态,中间区域保存类信息。这样一次对象头访问就能拿到类、内联引用计数、 是否有弱引用、是否有关联对象、是否可能有 C++ 析构等信息。 为什么要 nonpointer isa 对象绝大多数时候引用计数很小,把计数放进 isa 可让 retain/release 走原子 CAS 快路径,避免进入全局散列表和锁。 为什么仍需要 SideTable isa 空间有限。引用计数溢出、raw isa 对象、弱引用表,以及 deallocating 状态的兼容存储,都需要 SideTable。 raw isa 的存在原因 类对象始终使用 raw pointer isa;某些类通过 instancesRequireRawIsa() 或 运行时配置禁用 nonpointer isa;测试 rawisa.m 还覆盖了 __DATA,__objc_rawisa 段禁用 nonpointer isa 的场景。 ...

June 1, 2026

fishhook 源码导读

fishhook 由 Facebook 于 2013 年开源,用于在运行时动态重绑定(rebind)Mach-O 中被 dyld 绑定的 C 符号,是 iOS/macOS 逆向与底层埋点(malloc 追踪、双 close 检测、网络 socket 拦截、NSLog 重定向等)领域的"瑞士军刀"。 一、fishhook 是什么 一句话定义:fishhook 通过修改 Mach-O 镜像中 __DATA(_CONST) 段里 __la_symbol_ptr / __nl_symbol_ptr 两个 Section 中的函数指针,把对外部 C 符号(如 open、close、NSLog)的调用重定向到自定义的替换函数,同时保留原函数指针供回调使用。 它提供的能力类似 macOS 上 DYLD_INTERPOSE 宏(编译期 interpose),但: 运行时生效:不需要重新编译依赖库,随时可以在 App 启动后对系统库的 C 函数下钩子; 支持动态加载的 image:注册后对后续 dlopen 加载的 image 同样生效; 对业务零侵入:不改 App 本身的代码段,也不破坏符号表,只是重写 GOT/stub 里的一个指针。 但它也有非常明确的能力边界: 能力 是否支持 Hook 通过 dyld 动态绑定的外部 C 函数(libSystem、CoreFoundation 里的 C API、objc_msgSend 等) 是 Hook Objective-C 方法(-[NSString length]) 否(要用 Method Swizzling) Hook 当前 image 内部的 C 函数(静态链接、static、内联) 否(它不走 __la_symbol_ptr,直接是段内相对寻址) Hook Swift 方法(除非显式 @_cdecl) 否 Hook 已经解析并内联到寄存器里的符号(如 LTO 后消失的符号) 否 记住这条原则:fishhook 的作用域 = 被 dyld 绑定为"间接符号"(Indirect Symbol)的 C 符号。 ...

May 2, 2026

Objective-C weak 指针作用与实现原理

objc4 runtime zeroing weak SideTable weak_table_t Objective-C weak 指针:作用与实现原理 __weak 的核心价值是“引用对象但不拥有对象”:它不会增加引用计数;当对象开始销毁时,runtime 会把所有指向它的 weak 槽位自动写成 nil,避免野指针。这一页按 objc4 源码中的真实路径解释它如何登记、读取、搬迁和清零。 阅读顺序 weak 解决什么问题 运行时数据模型 核心类型、字段、方法 关键操作流程 带注释源码片段 一遍记住 1. weak 的作用 不拥有对象__weak id x = obj; 不会 retain obj,所以不会延长对象生命周期。 自动归零对象销毁时,所有保存该对象地址的 weak 变量会被 runtime 写成 nil。 读时短暂保活读取 weak 时,runtime 会先尝试 retain,再 autorelease,保证表达式使用期间对象不被并发释放。 拒绝无效目标目标正在 dealloc 或类不支持 weak 时,形成 weak 引用会失败或崩溃,取决于入口函数。 **一句话模型:**strong 指针管“拥有关系”,weak 指针管“可观察关系”。delegate、反向引用、缓存回调对象通常适合 weak,因为它们不应该阻止真实所有者释放对象。 2. 运行时数据模型 weak 表不是全局一把大表直接裸用,而是挂在分片的 SideTable 上。runtime 用对象地址选择某个 SideTable,在该表内维护引用计数辅助信息和 weak 表,并用同一把锁保护它们。 ...

June 1, 2026

CocoaPods 源码导读:从下载到工程集成

本文是系列第三篇,基于 CocoaPods 1.16.2 源码。上一篇我们停在 Analyzer 产出 AnalysisResult,本文继续走完 download_dependencies → validate_targets → generate_pods_project → integrate_user_project → write_lockfiles 这五个阶段。 上一篇:从命令到依赖求解 · 首篇:架构总览 一、全景:从 spec 到可编译的工程 先把本文要走的流程重新放一遍,让你带着地图读代码: flowchart TB A[AnalysisResult] --> B[download_dependencies] B -->|per pod| B1[PodSourceDownloader] B1 -->|命中| C1[Downloader::Cache] B1 -->|未命中| C2[cocoapods-downloaderGit/HTTP/SVN/...] C2 --> C3[rsync → Pods/<name>/] B -->|per pod| B2[PodSourceInstaller] B2 --> B3[PodSourcePreparerprepare_command] B2 --> B4[PodDirCleaner按 spec.source_files 裁剪] B --> D[validate_targetsTargetValidator#validate!] D --> E[generate_pods_project] E --> E1[ProjectCacheAnalyzer增量识别要重生成的 target] E --> E2[SinglePodsProjectGenerator#generate!] E2 --> E21[FileReferencesInstaller] E2 --> E22[PodTargetInstaller] E2 --> E23[AggregateTargetInstaller] E2 --> E24[xcconfig / modulemap / umbrella / info.plist / dummy.m] E --> E3[PodsProjectWriter#write!写盘前触发 Podfile post_install] E --> F[integrate_user_project] F --> F1[create_workspace写 .xcworkspace] F --> F2[deintegrate_removed_targets] F --> F3[TargetIntegrator#integrate!xcconfig / frameworks / scripts] F --> G[write_lockfiles] G --> G1[Podfile.lock] G --> G2[Pods/Manifest.lock] G --> H[perform_post_install_actionsplugin post_install hook] 对应的 Installer#install! 片段: ...

May 2, 2026

objc4 Autorelease 一页讲透

objc4 runtime guide Autorelease 的作用和实现原理 一句话:autorelease 是“延迟一次 release”的机制。对象先被登记到当前线程的自动释放池中, 等池子 pop 时再统一发送 release,从而让返回值和临时对象可以跨过当前语句或当前函数继续存活一小段时间。 核心文件:runtime/NSObject.mm 数据结构:AutoreleasePoolPage 线程隔离:TLS hot page 边界标记:POOL_BOUNDARY 先建立正确心智模型 autorelease pool 不是一堆对象的容器类。 在 objc4 里,它更像每个线程私有的一条栈。栈里放两类指针: 普通对象指针,以及表示池边界的 POOL_BOUNDARY。每次 push 池,就压入一个边界并返回这个边界地址作为 token。 每次 autorelease 对象,就把对象指针压到 hot page 的 next 位置。pop 时拿 token 找到边界, 把边界之上的对象按后进先出的顺序逐个 objc_release。 它解决什么问题 **返回 +0 对象:**被调用方可以返回一个“当前调用者可用,但不归调用者拥有”的对象。 **批量释放临时对象:**循环、事件回调、线程入口可以用池边界控制临时对象峰值。 兼容手动引用计数:retain 表示拿所有权,release 表示交回所有权,autorelease 表示稍后自动交回。 **配合 ARC 优化:**运行时和编译器会尝试消除返回值中的多余 retain/autorelease。 一张图看懂栈变化 假设外层池中 autorelease 了 A,又进入内层池 autorelease 了 B、C。内层 pop 只释放 B、C,不影响外层 A。 push 外层 ...

June 1, 2026

CocoaPods 源码导读:从命令到依赖求解

本文是系列第二篇,基于 CocoaPods 1.16.2 源码。我们从 bin/pod 进场,走完 CLAide 的命令分发、Podfile 的 DSL 求值、Analyzer 的七步分析、Resolver + Molinillo 的回溯求解,最后交付 AggregateTarget/PodTarget 给下一篇讲的下载与集成。 上一篇:架构总览 · 下一篇:从下载到工程集成 运行示例贯穿全文:pod install --repo-update 一、bin/pod:16 行的入口 CocoaPods 的可执行文件非常薄,核心逻辑全在 require 'cocoapods' 之后的 Pod::Command.run: # CocoaPods/bin/pod : 24 if $PROGRAM_NAME == __FILE__ && !ENV['COCOAPODS_NO_BUNDLER'] ENV['BUNDLE_GEMFILE'] = File.expand_path('../../Gemfile', __FILE__) require 'rubygems' require 'bundler/setup' $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) elsif ENV['COCOAPODS_NO_BUNDLER'] require 'rubygems' gem 'cocoapods' end STDOUT.sync = true if ENV['CP_STDOUT_SYNC'] == 'TRUE' require 'cocoapods' if profile_filename = ENV['COCOAPODS_PROFILE'] # 开 ruby-prof 做性能剖析 # ... reporter.new(RubyProf.profile { Pod::Command.run(ARGV) }).print(io) else Pod::Command.run(ARGV) end 这里有两个工程化细节值得留意: ...

May 2, 2026

Objective-C 关联对象 Associated Objects 作用与实现原理

objc4 runtime Associated Objects objc-references.mm 关联对象 Associated Objects 作用与实现原理 关联对象让调用方在不改类布局、不新增 ivar 的情况下,把一组 key - value 挂到任意 Objective-C 对象上。 objc4 的实现不是把字段塞进对象内存,而是用一张运行时全局表保存“对象地址、key、关联值和内存管理策略”的映射。 入口 APIobjc_setAssociatedObject objc_getAssociatedObject 核心存储AssociationsHashMap ObjectAssociationMap 值封装ObjcAssociation policy + value 清理时机objc_destructInstance objc_disposeClassPair 目录 作用与使用边界 公开 API 与 policy 核心结构与字段 set / get / remove 流程 对象销毁与特殊关联 禁止关联对象的场景 测试覆盖点 作用与使用边界 关联对象主要服务于分类、框架扩展、运行时补充状态等场景:调用方只有对象指针和一个稳定 key,就能为对象保存额外值。 这避免了修改类的 ivar 布局,也让分类可以模拟“存储属性”。 适合做什么 给现有类或分类补充少量状态。 把辅助对象、回调 block、缓存值挂到宿主对象。 用唯一地址作为 key,避免和其他调用方冲突。 不适合做什么 高频读写的核心数据结构。 需要类型系统和对象布局明确表达的状态。 清空他人关联值,尤其是随意调用 objc_removeAssociatedObjects。 运行时代价 每次访问都需要全局关联表查询。 set / get / remove 通过 AssociationsManagerLock 串行保护表。 对象析构时可能需要额外释放所有关联值。 **关键结论:**关联对象是“外置 side table”,不是对象内的 ivar。对象只保存一个“可能有 associated objects”的快速标记,真正的 key/value 在全局表里。 ...

June 1, 2026