Objective-C Category 装载与附加机制

objc4 runtime Category 装载与方法、协议、属性附加 Category 的本质不是“修改类结构体”,而是在镜像加载和类实现化时,把分类携带的方法列表、协议列表、属性列表合并到目标类或元类的可变列表视图中。objc4 同时要处理未实现类、stub class、dyld 预附加列表、方法缓存失效和 +load 顺序。 runtime/objc-runtime-new.mm runtime/objc-runtime-new.h runtime/objc-loadmethod.mm test/category.m 等测试 目录 作用 实现原理总览 核心结构和字段 关键流程 带注释核心代码 测试揭示的行为 作用 扩展类行为 分类把实例方法挂到类,把类方法挂到元类,让现有类获得新 selector 或覆盖既有 selector。 扩展反射信息 分类的协议和属性参与 class_copyProtocolList、class_getProperty 等运行时查询。 保持加载顺序 后加载的分类应优先被方法查找看到,所以列表附加采用“前插”。 支持动态镜像 bundle、共享缓存、stub class 和未实现类都能在不同时间点接收分类。 关键结论:分类附加不是把方法逐个复制进类定义,而是把 method_list_t、protocol_list_t、property_list_t 这类“列表”挂到 class_rw_ext_t 的数组视图前端。 实现原理总览 Mach-O 镜像 __objc_catlist / __objc_catlist2 中保存 category_t *。runtime 通过 header_info 知道分类来自哪个镜像。 → 分类分流 目标类未实现则暂存到 unattachedCategories;目标类已实现则立即 attachCategories。 → 列表前插 attachLists 把新增列表放在旧列表前面,方法查找和反射会先看到分类列表。 依据:load_categories_nolock 分流逻辑见 runtime/objc-runtime-new.mm:3585-3717;列表前插见 runtime/objc-runtime-new.h:2021-2105。 ...

June 1, 2026

Kingfisher源码导读

Kingfisher 是一款纯 Swift 图片下载和缓存库,支持 iOS、macOS、tvOS、watchOS 和 visionOS 全平台。当前最新版本为 8.8.0(2026年3月),已全面适配 Swift 6 严格并发模型。本文基于 v8.x 源码进行分析。 一、整体架构 Kingfisher 采用协议导向的模块化设计,核心由四大组件构成: graph TB subgraph "UI层" A["UIImageView.kf / KFImage"] end subgraph "管理层" B["KingfisherManager"] end subgraph "功能层" C["ImageDownloader"] D["ImageCache"] E["ImageProcessor"] end subgraph "存储层" F["MemoryStorage.Backend(NSCache)"] G["DiskStorage.Backend(FileManager)"] end A -->|"retrieveImage"| B B -->|"下载"| C B -->|"缓存"| D B -->|"处理"| E D --> F D --> G 源码目录结构: Sources/ ├── General/ # KingfisherManager, KingfisherOptionsInfo, Resource, Source ├── Networking/ # ImageDownloader, SessionDelegate, ImagePrefetcher ├── Cache/ # ImageCache, MemoryStorage, DiskStorage ├── Image/ # ImageProcessor, 图片格式处理, 滤镜 ├── Extensions/ # UIKit/AppKit 扩展 (ImageView+Kingfisher, UIButton+Kingfisher) ├── SwiftUI/ # KFImage, KFAnimatedImage, ImageBinder ├── Views/ # AnimatedImageView └── Utility/ # 辅助工具类 二、命名空间设计 — KingfisherWrapper Kingfisher 使用 kf 命名空间来避免对系统类型的污染,这是 Swift 社区广泛使用的一种模式。 ...

May 2, 2026

Swift 面试:编译流程、Runtime 与元数据源码解析

Swift 面试:编译流程、Runtime 与元数据源码解析 Swift 面试里问编译流程和 Runtime,通常不是想听一串名词,而是想看你能不能把 源码 -> AST -> SIL -> 优化 -> IRGen -> LLVM -> Runtime Metadata 串起来。 这篇文章按面试题展开,把答案落到 Swift 编译器源码里的 SILGen、SILOptimizer、IRGen、Metadata、Value Witness Table、Protocol Witness Table 和 HeapObject。 面试高频问题 Swift 源码从 .swift 到机器码经历哪些阶段? AST、SIL、LLVM IR 分别负责什么? 为什么 Swift 需要 SIL,而不是直接生成 LLVM IR? SILGen 做什么?SILOptimizer 做什么? IRGen 为什么要生成 Metadata 和 Witness Table? Swift Runtime Metadata 保存了哪些信息? Value Witness Table 是什么? Protocol Witness Table 和 class vtable 有什么区别? class、struct、enum 在 Runtime 表示上有什么差异? HeapObject 和 Metadata 有什么关系? 30 秒回答版 Swift 编译流程可以简化为: ...

June 16, 2026

Objective-C +load 调度:load_images 与 call_load_methods

objc4 runtime +load 调度:从 load_images 到 call_load_methods 这页解释 objc4 如何在 dyld 映射镜像后发现、排队并调用 Objective-C 的 +load。 重点是类与分类的顺序、父类优先、递归和重入处理,以及 runtimeLock 与 loadMethodLock 的边界。 入口:dyld callback 队列:loadable_classes / loadable_categories 调度:call_load_methods 测试:load*.m 目录 作用 实现原理 核心结构和关键函数 关键流程 带注释代码片段 测试体现的语义 作用 +load 是 Objective-C 运行时在类或分类被装入进程时主动调用的类方法。 它不依赖消息发送触发,也早于普通的 +initialize。objc4 的任务不是简单遍历所有方法并调用, 而是在 dyld 映射镜像、runtime 完成类注册和分类附着后,按照语言语义和装载依赖稳定地调用。 装载时机 dyld 通过 _dyld_objc_register_callbacks 注册 runtime 回调。镜像映射后先走 map_images,随后 load_images 处理该镜像中的非懒加载类和分类。 顺序语义 类 +load 必须父类优先;所有当前可调用的类 +load 先于分类 +load;分类必须等宿主类已经完成自己的 +load。 重入安全 +load 内部可能 dlopen 新镜像,再次进入 load_images。objc4 让内层调用只排队,真正调用由最外层 call_load_methods 收尾。 实现原理 objc4 将 +load 分成“发现”和“调用”两阶段。发现阶段需要持有 runtimeLock, 因为它要读取和实现类、解析分类、查找元类方法列表。调用阶段释放 runtimeLock, 只持有递归的 loadMethodLock,因为用户代码可能执行任意 Objective-C 行为并重新映射镜像。 ...

June 1, 2026

RxSwift源码导读

RxSwift 是 ReactiveX 在 Swift 上的实现,提供了一套完整的响应式编程框架。它通过 Observable 序列和操作符组合的方式,简化了异步编程、事件处理和数据流管理。本文基于 v6.10.2(2026年3月发布)源码进行分析。 一、整体架构 RxSwift 项目采用模块化设计,包含五个独立模块: graph TB subgraph "核心层" A["RxSwift核心响应式框架"] end subgraph "扩展层" B["RxCocoaUIKit/AppKit绑定"] C["RxRelayRelay封装"] end subgraph "工具层" D["RxBlocking阻塞操作"] E["RxTest测试工具"] end B --> A C --> A D --> A E --> A 源码目录结构: RxSwift/ ├── Concurrency/ # 并发工具(AsyncLock等) ├── Disposables/ # 资源释放(DisposeBag、CompositeDisposable等) ├── Extensions/ # Swift扩展 ├── Observables/ # Observable实现(Create、Map、Filter、Merge等) ├── Observers/ # Observer实现(AnonymousObserver、ObserverBase等) ├── Platform/ # 平台适配(原子操作、锁等) ├── Schedulers/ # 调度器(MainScheduler、SerialDispatchQueueScheduler等) ├── Subjects/ # Subject实现(PublishSubject、BehaviorSubject等) ├── SwiftSupport/ # Swift语言支持 ├── Traits/ # 特化序列(Single、Completable、Maybe、Infallible) ├── Observable.swift # Observable基类 ├── ObservableType.swift # Observable协议 ├── ObserverType.swift # Observer协议 ├── Event.swift # 事件枚举 ├── Disposable.swift # Disposable协议 ├── Binder.swift # UI绑定 ├── AnyObserver.swift # 类型擦除Observer └── Reactive.swift # .rx命名空间 语言构成: 纯 Swift 实现,100% Swift 代码。 ...

May 2, 2026

+initialize 懒初始化与并发状态机

objc4 runtime 懒初始化 并发等待 fork safety +initialize 懒初始化与并发状态机 +initialize 是 Objective-C 运行时在类第一次被真正使用时触发的类级初始化钩子。 objc4 的实现重点不是“调用一个方法”,而是用每个类的递归初始化锁、元类上的状态位、线程本地的初始化列表和 fork 子进程保护规则,保证父类优先、单线程执行、并发可等待、重入不死锁。 目录 作用 实现原理 核心状态字段 入口与触发 并发状态机 关键流程 测试依据 作用 +initialize 的语义是:在某个类或其子类第一次收到会触发初始化的消息前,运行时先给该类发送一次 +initialize。 这让类可以延迟建立全局状态、注册方法、准备缓存或执行只依赖本类的初始化逻辑。 懒触发 objc_getClass() 只查找类,不触发初始化;普通消息发送、部分运行时查询、autorelease 返回值路径和弱引用相关路径可能需要先完成初始化。 父类优先 子类开始初始化前,运行时递归确保父类已经初始化,避免类层次中间被并发线程插入造成死锁。 一次完成 同一类只有一个线程真正发送 +initialize;其他线程要么等待,要么在同一初始化线程重入时直接放行。 实现原理 objc4 把 +initialize 做成一个按需进入的并发状态机:消息查找路径发现目标类未初始化时,先把类 realize,再释放 runtimeLock,进入 initializeNonMetaClass()。 后者用父类递归、每类递归锁、元类状态位和线程本地初始化列表协调所有线程。真正执行用户代码的线程设置 INITIALIZING,发送 +initialize,最后在 @finally 中切换到 INITIALIZED;其他线程通过同一把类锁等待状态稳定。 这个设计的核心约束是单调性:类状态只从“未初始化”前进到“初始化中”,再前进到“已初始化”。即使 +initialize 抛异常,状态机也会完成收尾;即使发生重入,只有当前初始化线程能绕过等待;即使 fork 发生在多线程初始化期间,子进程也只允许 trivial 初始化继续,其他自定义初始化会主动终止。 核心类/结构状态字段 初始化状态存在类对象的元类标志位里。源码注释明确约束:初始时 RW_INITIALIZING 和 RW_INITIALIZED 都未设置;初始化中只设置前者;完成后清除前者并设置后者;两者永不同时为真,且 RW_INITIALIZED 一旦设置就不会清除。 ...

June 1, 2026

SDWebImage源码导读

SDWebImage 是 iOS 生态中历史最悠久、最流行的异步图片下载缓存库,由 Olivier Poitrey 在 2009 年创建,如今由社区维护。截至 2026 年 4 月,GitHub Star 数已超过 25.6k,最新稳定版本为 5.21.7(2026 年 2 月)。5.21 系列引入了 HDR 图片编解码支持,并持续完善线程安全和 iOS 26 的兼容性。本文基于 master 分支源码(Objective-C 实现)进行分析。 一、整体架构 SDWebImage 采用协议导向 + 责任链的模块化设计,核心由五大子系统构成:Manager 协调层、Cache 缓存层、Downloader 下载层、Coder 编解码层和 UI 扩展层。 graph TB subgraph "UI 扩展层" A["UIImageView+WebCacheUIButton+WebCacheUIView+WebCache"] end subgraph "管理层" B["SDWebImageManager"] end subgraph "缓存层 SDImageCache" C1["SDMemoryCache(NSCache + NSMapTable)"] C2["SDDiskCache(NSFileManager)"] end subgraph "下载层" D1["SDWebImageDownloader"] D2["SDWebImageDownloaderOperation(NSOperation)"] end subgraph "编解码层 SDImageCodersManager" E1["SDImageIOCoder"] E2["SDImageGIFCoder"] E3["SDImageAPNGCoder"] E4["SDImageHEICCoderWebP/AVIF (插件)"] end subgraph "辅助层" F["SDImageTransformerSDWebImageCacheKeyFilterSDWebImageOptionsProcessorSDCallbackQueue"] end A -->|sd_setImageWithURL| B B --> C1 B --> C2 B --> D1 D1 --> D2 D2 --> E1 B --> F 源码目录结构: ...

May 2, 2026

objc4 Selector / SEL 注册、唯一化与方法名查找

Selector / SEL 注册、唯一化与方法名查找 在 objc4 中,SEL 的关键价值不是承载一段复杂对象,而是把方法名字符串 “驻留”为全进程唯一的指针。注册、镜像加载 fixup、方法列表排序、方法查找和消息缓存都建立在这个事实上: 同名 selector 最终拥有同一个地址,因此比较 selector 可以退化为一次指针比较。 作用 统一方法名身份 编译器、Mach-O 镜像、动态注册 API 和运行时内部方法列表都可能产生方法名。selector 唯一化后,同一个方法名只对应一个 SEL。 让查找足够便宜 方法列表和缓存查找不需要反复比较字符串。命中条件基本都是 method.name == sel 或 bucket.sel == _cmd。 支撑 dyld 预优化 共享缓存中的 selector 可以由 dyld 提供内建唯一值;未优化镜像在 _read_images 中被 runtime 修正到唯一值。 对外头文件把 SEL 声明为不透明类型:typedef struct objc_selector *SEL;。 但在当前实现中,runtime 会把 selector 当成方法名 C 字符串指针使用,sel_getName() 直接把 SEL 转回 const char *。 核心结构、函数与字段 SEL 公开为不透明 selector 指针,实际唯一值是某个方法名字符串的地址。来源:runtime/objc.h。 namedSelectors ExplicitInitDenseSet<const char *>,保存非 dyld 内建 selector 的唯一字符串指针。来源:runtime/objc-sel.mm。 selLock 保护 selector 表。sel_registerName() 自行加锁;镜像加载和方法列表 fixup 常在外层持锁后调用 sel_registerNameNoLock()。 _sel_searchBuiltins() 在支持预优化时向 dyld 查询共享缓存内建 selector,优先返回 dyld 已唯一化的值。 sel_registerName() 注册并返回 selector;若已存在则返回既有唯一值;会复制传入字符串。 sel_lookUpByName() 只查找,不创建。未注册返回 NULL,测试 test/sel.m 明确覆盖该行为。 method_t::name 方法条目的 selector 字段。方法列表 fixup 后,这个字段应是唯一化的 SEL。 cache_t / bucket_t 消息缓存以 SEL 为 key,桶保存 SEL + IMP,hash 直接来自 selector 地址。 实现原理 selector interning 的核心是“先按名字查找唯一槽,再把槽里的字符串地址作为 SEL 返回”。 对 dyld 共享缓存内建 selector,runtime 先问 dyld;对普通 selector,runtime 使用 namedSelectors 保存唯一字符串。 ...

June 1, 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

objc4 Protocol 结构、注册与查询

目录 1. Protocol 的作用 2. 核心结构 3. read_images 读取协议 4. remap 与重复协议 5. 查询与 conforms 判断 6. 动态创建与注册 7. 测试体现的行为 8. 总结 1. Protocol 的作用 在 objc4 中,Protocol * 并不是一个只有名字的轻量句柄,而是运行时管理的一类 Objective-C 对象。 它承载协议名、继承的协议列表、必需/可选方法列表、实例/类属性列表等元数据。类、分类和协议自身都可以引用这些对象。 对外 API 位于 runtime/runtime.h 的 “Working with Protocols” 区域,例如 objc_getProtocol、objc_copyProtocolList、protocol_conformsToProtocol、 protocol_getMethodDescription、objc_allocateProtocol 与 objc_registerProtocol。 runtime/Protocol.mm 中的 @implementation Protocol 则把老式 Objective-C 消息转接到这些 C API。 关键心智模型:编译器把协议写进镜像的 __objc_protolist 等段;runtime 在 _read_images 中发现并安装协议; 后续所有查询都尽量通过“协议名 => 胜出的协议对象”来回到唯一的、当前有效的定义。 2. 核心结构 当前实现中的底层结构定义在 runtime/objc-runtime-new.h。公开的 Protocol * 在实现里经常通过 newprotocol(p) 转成 protocol_t * 使用。 // runtime/objc-runtime-new.h,保留关键字段并加注释 typedef uintptr_t protocol_ref_t; // 尚未 remap 的 protocol_t * struct protocol_t : objc_object { const char *mangledName; // 协议的运行时名字;Swift v1 可能是 mangled 名 struct protocol_list_t *protocols; // 该协议继承/组合的其他协议 method_list_t *instanceMethods; // @required 实例方法 method_list_t *classMethods; // @required 类方法 method_list_t *optionalInstanceMethods; // @optional 实例方法 method_list_t *optionalClassMethods; // @optional 类方法 property_list_t *instanceProperties; // required instance properties uint32_t size; // 磁盘结构大小;用于判断尾部字段是否存在 uint32_t flags; // fixed-up、canonical 等运行时标志 // 以下字段不是所有磁盘协议结构都有,访问前必须看 size。 const char **_extendedMethodTypes; // 扩展类型编码 const char *_demangledName; // Swift demangled 名称缓存 property_list_t *_classProperties; // 类属性列表 }; struct protocol_list_t { uintptr_t count; // 历史原因:count 是指针宽度 protocol_ref_t list[0]; // 变长数组;元素可能需要 remap }; 关键字段与方法 mangledName 全局协议表的 key。objc_getProtocol 先按原名查,再查 dyld 预优化表,再尝试 Swift v1 mangled 等价名。 protocols 协议继承关系。protocol_conformsToProtocol 会递归遍历它;protocol_copyProtocolList 只复制直接继承项。 四个方法列表 按 required/optional 与 instance/class 分成四组。protocol_copyMethodDescriptionList 只返回当前协议直接声明的方法,不包含父协议。 size 兼容小协议或旧 ABI 的关键字段。protocolSmall.m 手工构造较小结构,验证访问尾部字段前必须判定字段是否存在。 flags 高位用于 PROTOCOL_FIXED_UP_* 与 PROTOCOL_IS_CANONICAL,标记是否已修正以及共享缓存中的 canonical 定义。 Protocol 类 runtime/Protocol.h 只暴露不可直接使用的 @interface Protocol; Protocol.mm 中 -conformsTo:、-name、-isEqual: 分别调用 protocol_conformsToProtocol、protocol_getName 与 protocol_isEqual。 ...

June 1, 2026