适配器模式

定义 适配器模式(Adapter Pattern)是一种结构型设计模式,它将一个类的接口转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作。 适配器模式的核心思想是:通过一个中间层(适配器)来使不兼容的接口能够一起工作,而无需修改原有代码。 为什么需要适配器模式 适配器模式要解决的核心问题是:让接口不兼容的类能够一起工作,而不需要修改原有代码。 问题场景:假设我们的App原来使用的是自己封装的网络库,现在想要切换到Alamofire,但项目中有大量地方直接使用了旧接口。 // 旧的网络接口 - 项目中到处都在使用 protocol OldNetworkClient { func get(url: String, callback: @escaping (Data?, Error?) -> Void) func post(url: String, body: Data?, callback: @escaping (Data?, Error?) -> Void) } // 新引入的Alamofire有完全不同的接口 // AF.request(url).response { response in ... } 最直接的方式是修改所有调用处: // 需要修改项目中所有使用旧接口的地方 // 从: oldClient.get(url: "https://api.example.com/users") { data, error in ... } // 改为: AF.request("https://api.example.com/users").response { response in ... } 这种方式有什么问题? 工作量巨大:需要修改项目中所有使用网络请求的地方 风险高:大量修改容易引入bug 难以回退:如果新库有问题,很难切换回旧实现 违反开闭原则:修改了大量现有代码 适配器模式的解决思路: 创建一个适配器,让新库"伪装"成旧接口,现有代码无需修改: // 适配器 - 让Alamofire适配旧接口 class AlamofireAdapter: OldNetworkClient { func get(url: String, callback: @escaping (Data?, Error?) -> Void) { // 内部使用Alamofire,但对外暴露旧接口 AF.request(url).response { response in callback(response.data, response.error) } } func post(url: String, body: Data?, callback: @escaping (Data?, Error?) -> Void) { AF.request(url, method: .post, parameters: nil, encoding: URLEncoding.default) .response { response in callback(response.data, response.error) } } } // 现有代码完全不需要修改 let client: OldNetworkClient = AlamofireAdapter() client.get(url: "https://api.example.com/users") { data, error in // 原有的处理逻辑保持不变 } 适配器模式的好处: ...

June 21, 2026

Claude Code 源码导读:Agent 设计详解

Claude Code 的 agent 到底是怎么被设计成一个能长时间工作、能分派子 agent、能记忆、能压缩上下文、也能可靠停下来的系统的? Claude Code 最值得研究的地方,不是它“能调用工具”,而是它把模型、工具、记忆、上下文、后台任务、hook 和权限系统编成了一个稳定的 agent harness。 一句话概括:Claude Code 的 agent 不是一个模型循环,而是三层循环叠在一起:主 query 循环负责推理和工具回灌;工具循环负责受控执行和并发;任务循环负责子 agent、后台 agent、记忆整理和长任务生命周期。 flowchart TD User["用户输入 / 队列消息"] --> Query["query.ts 主循环"] Query --> Model["流式模型调用"] Model -->|没有 tool_use| StopPath["停止路径stop hooks / token budget / completed"] Model -->|产生 tool_use| ToolLoop["工具执行循环"] ToolLoop --> Attach["附件注入记忆 / 变更文件 / 任务通知 / 技能"] Attach --> Query Query --> Compact["上下文治理tool budget / microcompact / autocompact / reactive compact"] Query --> Memory["记忆系统CLAUDE.md / AutoMem / Session Memory"] Query --> AgentTool["AgentTool"] AgentTool --> SubAgent["runAgent 子循环"] SubAgent --> LocalTask["LocalAgentTask前台 / 后台 / 可恢复"] LocalTask --> Attach 一、核心源码地图 先把关键文件放在桌面上: ...

June 21, 2026

iOS中import详解

源码版本说明:本文涉及的源码基于 LLVM/Clang 23.0.0 开发版本(llvm-project main分支,commit: 301c0d91b558,2026-01-16)。不同版本的实现细节可能略有差异,但核心机制保持一致。 在Objective-C开发中,头文件引用是日常开发中最基础的操作之一。本文将从Clang编译器的角度,深入讲解iOS中各种import方式的区别、底层查找原理以及最佳实践。 引用方式 语法示例 适用场景 #include #include "Header.h" C/C++传统方式 #import #import "Header.h" Objective-C项目内部引用 #import #import <Framework/Header.h> 系统/第三方Framework @import @import Foundation; Clang Modules方式 PCH Prefix Header文件 预编译公共头文件 #include vs #import #include的工作原理 #include是C/C++中的头文件引用方式,它的工作原理非常简单:预处理器会将目标头文件的内容原封不动地复制粘贴到当前文件中。 这种方式存在一个问题:重复引用。如果多个文件都include了同一个头文件,或者存在循环引用,会导致编译错误。传统的解决方案是使用Include Guard: // Header.h #ifndef HEADER_H #define HEADER_H // 头文件内容 #endif 现代编译器还支持#pragma once指令,功能相同但更简洁: // Header.h #pragma once // 头文件内容 #import的增强 #import是Objective-C对#include的封装和增强。它在#include的基础上增加了一层判重逻辑,自动防止同一个头文件被重复引用。 // BClass.m #import "AClass.h" #import "AClass.h" // 第二次import会被自动忽略 双引号 vs 尖括号 在日常开发中,我们经常会看到两种不同的引用方式: #import "MyClass.h" // 双引号形式 #import <UIKit/UIKit.h> // 尖括号形式 它们的核心区别在于搜索路径的范围不同: ...

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

RunLoop

RunLoop 是 iOS/macOS 中用于管理线程的事件循环机制。它的核心作用是让线程在有任务时处理任务,没有任务时进入休眠状态,从而避免线程退出并节省 CPU 资源。 简单来说,RunLoop 就是一个 do-while 循环: // 伪代码 void CFRunLoopRun() { do { // 处理各种事件源 // 如果没有事件,线程休眠 // 有事件时被唤醒处理 } while (running); } RunLoop 与线程的关系 每个线程都有唯一对应的 RunLoop 对象 主线程和子线程的 RunLoop 都采用懒加载机制,在第一次获取时才创建 主线程的 RunLoop 由 UIApplicationMain 内部首次获取并启动(调用链:UIApplicationMain → GSEventRunModal → CFRunLoopRunSpecific) RunLoop 与线程是一一对应的,存储在一个全局字典(__CFRunLoops)中 // 获取当前线程的 RunLoop CFRunLoopRef runLoop = CFRunLoopGetCurrent(); NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; // 获取主线程的 RunLoop CFRunLoopRef mainRunLoop = CFRunLoopGetMain(); NSRunLoop *mainRunLoop = [NSRunLoop mainRunLoop]; RunLoop 的核心组成 RunLoop 包含以下几个核心概念: 1. RunLoop 对象(CFRunLoopRef) RunLoop 对象本身,管理整个事件循环。在代码层面对应 CFRunLoopRef 类型。 ...

May 27, 2026

设计模式概述

什么是设计模式? 设计模式(Design Pattern)是软件开发中反复出现问题的经典解决方案。它们是前人在大量实践中总结出的、被证明有效的代码设计经验,可以帮助开发者编写出更加灵活、可维护、可复用的代码。 设计模式不是具体的代码,而是解决特定问题的思想和模板。正确运用设计模式可以: 提高代码的可读性和可维护性 增强代码的可复用性 降低模块间的耦合度 提供通用的设计词汇,便于团队沟通 设计模式的起源 设计模式的概念最早由"四人帮"(Gang of Four,简称GoF)在1994年出版的《Design Patterns: Elements of Reusable Object-Oriented Software》一书中系统化提出。书中总结了23种经典设计模式,这些模式至今仍是软件设计的重要参考。 设计模式分类 GoF将23种设计模式分为三大类: 创建型模式(Creational Patterns) 创建型模式关注对象的创建机制,旨在以合适的方式创建对象,而不是直接使用new操作符。这类模式使得代码在创建对象时更加灵活。 模式 描述 iOS常见应用 单例模式 确保一个类只有一个实例 UIApplication.shared、FileManager.default 工厂方法模式 定义创建对象的接口,由子类决定实例化哪个类 NSNumber的工厂方法 抽象工厂模式 创建一系列相关对象而无需指定具体类 UIKit组件创建 建造者模式 将复杂对象的构建与表示分离 URLComponents、AlertController 原型模式 通过复制现有实例创建新对象 NSCopying协议 结构型模式(Structural Patterns) 结构型模式关注类和对象的组合,用于形成更大的结构。这类模式帮助确保当系统的一部分发生改变时,整个系统不需要随之改变。 模式 描述 iOS常见应用 适配器模式 将一个类的接口转换成客户期望的另一个接口 协议适配、第三方库封装 桥接模式 将抽象部分与实现部分分离 平台无关的抽象设计 组合模式 将对象组合成树形结构以表示部分-整体层次结构 UIView层级结构 装饰器模式 通过包装对象在运行时动态添加职责 包装器对象、链式装饰 外观模式 为子系统提供一个统一的高层接口 SDK封装、模块门面 享元模式 运用共享技术有效支持大量细粒度对象 字体对象、颜色对象等可共享的不可变对象 代理模式 为其他对象提供一种代理以控制访问 NSProxy、虚拟代理、保护代理 行为型模式(Behavioral Patterns) 行为型模式关注对象之间的通信和职责分配,描述类或对象如何交互以及如何分配职责。 ...

May 27, 2026

值类型和引用类型的区别

定义 值类型 值类型是指变量直接存储自身字段或描述信息的类型。值类型具有值语义,当将一个值类型变量赋值给另一个变量时,语义上会得到一份独立的值;底层是否立即复制完整数据,取决于编译器优化和写时拷贝等实现。 引用类型 引用类型是指变量存储的是指向数据在内存中位置的引用(指针)。当将一个引用类型变量赋值给另一个变量时,两个变量指向同一块内存区域。 Objective-C中的类型分类 在Objective-C中: 值类型:基础数据类型(int、float、double、BOOL、char等)、结构体(struct)、枚举(enum) 引用类型:除基础数据类型之外的大部分类型,包括NSObject及其子类(NSString、NSArray、NSDictionary、自定义类等) Swift中的类型分类 Swift对类型的分类更加清晰: 值类型:基础数据类型(Int、Float、Double、Bool等)、字符串(String)、结构体(Struct)、枚举(Enum)、数组(Array)、字典(Dictionary)、集合(Set)等 引用类型:类(Class)、闭包(Closure)、Actor等 拷贝机制差异 值类型 - 值语义拷贝 每个值类型变量都有自己的字段存储 对一个变量的操作不会影响另一个变量 赋值语义上会得到独立副本;如果字段是引用类型,复制的是引用本身,底层也可能通过写时拷贝延迟真正的数据复制 引用类型 - 浅拷贝 引用类型在内存中有一个指向该位置的引用 引用类型的变量可以指向相同类型的数据 对一个变量进行的操作会影响另一变量所指向的数据 内存分配机制 需要注意:值类型/引用类型描述的是语义模型,不等价于栈/堆分配规则。Swift编译器会根据生命周期、逃逸分析、优化级别、协议类型包装、集合存储等因素决定具体放在哪里。 理解Swift对象和字段的内存位置,可以先记住三个规则: class实例本体通常在堆上,通过引用计数管理生命周期 struct/enum的字段通常内联存储在这个值本身所在的位置 class类型的字段存储的是对象引用,也就是一个指针,真实class实例仍然在堆上 常见存储位置 局部值类型变量:生命周期明确、未逃逸时,通常可以放在当前线程的栈区,甚至被优化到寄存器中 class实例:实例本体在堆区,局部变量中保存的是指向堆对象的引用 class里的struct属性:struct属性内联存储在class实例这块堆内存中 struct里的class属性:struct内部只保存class引用,真实class实例仍然在堆上 被逃逸闭包捕获的值类型可能随闭包上下文一起存储在堆区 Array、Dictionary、Set、String等写时拷贝值类型通常只有一小段描述信息是值本身,真实元素或字符缓冲区可能在堆上 协议类型(existential container)持有大值类型时,如果超过存在容器的内联缓冲区大小,会使用堆分配 indirect enum的间接关联值会通过堆上的盒子存储,常见于递归枚举 全局变量、静态变量不属于栈区,也不是普通意义上的堆对象,它们通常位于全局/静态存储区 栈区内存分配和销毁通常只需移动栈顶指针,成本较低;堆区更动态,但分配、释放和引用计数维护都有额外成本。 struct中包含class属性 class Dog { var name: String init(name: String) { self.name = name } } struct Person { var age: Int var dog: Dog } var p1 = Person(age: 18, dog: Dog(name: "Lucky")) var p2 = p1 p2.age = 20 p2.dog.name = "Max" print(p1.age) // 18 print(p1.dog.name) // Max 内存关系可以近似理解为: ...

May 10, 2026

Objective-C与Swift区别

语言设计 Objective-C 是C的超集,在C的基础上添加了Smalltalk风格的消息传递机制 对象间交互不通过调用方法而是发送消息,以实现面向对象编程和超高的动态性 动态性由 Objective-C Runtime 系统支持,可以在运行时修改类结构、方法交换等,详见 Runtime 弱类型语言,核心类型id可以指向任何Objective-C对象 Swift 从很多现代化语言中汲取精华,是一门偏重于安全、性能的现代化语言 Swift 也有自己的运行时系统(Swift Runtime),但设计哲学完全不同,更强调编译期优化 强类型语言,编译器在编译阶段进行严格的类型检查 语法特性 Objective-C 语法冗长,需要显式声明类型 命名通常较长且需要添加前缀避免冲突 需要单独的.h和.m文件声明接口和实现 引用其他文件/模块必须要import(Objective-C中import详解) 协议不支持默认实现 泛型是"轻量级泛型",仅用于编译时类型检查,运行时类型信息会被擦除 Swift 语法简洁,强大的类型推断减少冗余的类型声明 有命名空间(模块)的存在避免命名冲突 不需要单独的头文件声明接口 默认情况下变量必须初始化 引用同模块的其他文件不需要import(Swift中import详解) 协议支持默认实现、支持拓展,支持关联类型以实现泛型协议 值类型也可以遵守协议 泛型在编译时进行类型特化优化,支持完整的泛型元编程 内存管理 Objective-C 大部分都是引用类型,依赖引用计数管理内存 ARC在编译时自动插入retain、release、autorelease调用 64位系统使用优化的isa(Non-pointer isa),通过位域在isa中嵌入引用计数(extra_rc字段),溢出时使用SideTable存储 实例变量(ivar)会被Runtime自动初始化为0/nil Swift 引用类型也是依赖引用计数管理内存 存在大量的值类型,例如结构体、枚举、数组、字典等 非逃逸的值类型的内存存在栈区由系统进行自动管理 纯Swift类的引用计数直接存储在对象头部的RefCount字段中,无需SideTable,访问更高效 编译器在编译时就确保了初始化,不需要运行时统一清零 拓展:值类型和引用类型的区别 拓展:iOS中的内存管理 性能对比 Objective-C 基于消息传递机制本质上是动态派发,运行时查找方法实现,有一定开销 ARC管理引用计数也有开销 引用类型内存存在堆区,内存管理的性能开销会比栈区多很多 Swift 方法派发尽可能使用静态派发(如对final类/方法、私有方法、值类型方法的调用) 编译器可以内联优化,速度接近C 内联优化:比如A方法调用B方法,B方法调用C方法,代码编写调用A方法时编译器通过内联直接调用C方法的实现 值类型内存分配在栈区,性能开销相对较小 虽然值类型是深拷贝,Swift为了优化值类型性能引入写时拷贝(Copy-on-Write)机制,值类型只在写操作发生深拷贝,其他时候都是浅拷贝 安全性 Objective-C 弱类型(动态类型)占主导 核心类型id可以指向任何Objective-C对象 编译器几乎不做类型检查,会有运行时崩溃的风险(如向不响应某消息的对象发送该消息) nil消息发送不会崩溃但可能导致逻辑错误,不容易感知 局部变量不会自动初始化,可能包含栈上的垃圾数据(未定义行为) Swift 可选类型强制处理空值,编译器强制解包检查 强类型和编译时检查阻止了大量常见错误 变量必须初始化后才能使用,编译器在编译阶段强制检查,消除未定义行为 强制初始化使编译器能更精确地追踪变量生命周期,带来更多优化机会 数组越界等操作会在运行时触发明确的崩溃,便于定位问题 编程范式 Objective-C 主要是面向对象编程(OOP) 通过继承实现代码复用 依赖Runtime实现面向切面编程(AOP),如Method Swizzling Swift 拥抱多范式,对函数式编程、面向协议编程(POP)、声明式编程很友好 通过协议扩展提供默认实现,协议组合替代多继承 值类型(struct/enum)也可以遵循协议,不再局限于类 拓展:OOP、POP与AOP ...

May 8, 2026

mmap详解

mmap(Memory-mapped file,内存映射文件)是一种将文件或设备映射到进程地址空间的技术。通过mmap,可以像操作内存一样操作文件,是iOS开发中实现高性能IO的重要手段。 基本概念 什么是mmap mmap是一个POSIX系统调用,它在进程的虚拟地址空间中创建一个映射,将文件内容映射到内存地址。映射建立后,对该内存区域的读写操作会直接反映到文件上。 文件映射:将磁盘上的普通文件映射到内存,用于高效读写文件内容(iOS开发中最常用) 设备映射:将硬件设备的I/O内存(如显卡显存、网卡缓冲区)映射到用户空间,让程序可以像读写内存一样直接与硬件交互,常用于驱动开发和嵌入式系统 flowchart LR subgraph traditional["传统文件IO (read/write)"] direction LR U1["用户缓冲区"] <-- "② 数据拷贝" --> K1["内核缓冲区"] K1 <-- "① 磁盘读取" --> F1["磁盘文件"] end subgraph mmap_io["mmap 内存映射"] direction LR U2["虚拟地址空间映射区域"] <-. "直接映射零拷贝" .-> F2["磁盘文件"] end traditional ~~~ mmap_io 方式 数据流向 拷贝次数 传统IO 磁盘 → 内核缓冲 → 用户缓冲 2次 mmap 磁盘 ↔ 映射区域(直接访问) 0次 mmap的工作原理 flowchart TD A["调用mmap()"] --> B["内核在进程虚拟地址空间分配一段连续地址"] B --> C["建立虚拟地址到文件的映射关系(页表项指向文件页)"] C --> D["返回映射区域的起始地址"] D --> E["进程访问映射地址"] E --> F{"页面是否在内存中?"} F -- 否 --> G["触发缺页中断(Page Fault)"] G --> H["内核从磁盘读取对应页面到物理内存"] H --> I["更新页表,建立映射"] I --> J["进程继续访问"] F -- 是 --> J J --> K["读写操作直接作用于内存"] K --> L["内核适时将脏页写回磁盘"] API详解 mmap函数原型 #include <sys/mman.h> void *mmap( void *addr, // 建议的映射起始地址,通常传NULL让系统决定 size_t length, // 映射区域的长度(字节) int prot, // 内存保护标志(可读/可写/可执行) int flags, // 映射类型标志 int fd, // 文件描述符 off_t offset // 文件偏移量(必须是页大小的整数倍) ); // 返回值:成功返回映射区域的起始地址,失败返回MAP_FAILED 参数详解 prot(内存保护标志): ...

May 7, 2026

Swift底层原理-结构体、类和协议

在Objective-C底层原理-NSObject文章中,我们深入了解了Objective-C对象的底层实现。本文将探讨Swift中类和结构体的底层原理。 Swift数据结构的分类 Swift中的类根据是否继承自NSObject,在底层实现上存在显著差异: 继承自NSObject的Swift类:兼容Objective-C运行时,支持完整的Objective-C特性 纯Swift类:使用Swift原生运行时,性能更优但Objective-C互操作性受限 Swift中结构体是值类型,具有以下核心特征: 栈上分配内存(非逃逸情况) 值语义和完整拷贝 写时拷贝优化(COW) 静态方法派发 Swift类的底层实现 继承自NSObject的Swift类 当Swift类继承自NSObject时,必须兼容Objective-C的运行时系统,其底层实现与Objective-C对象高度一致。 内存布局 class Class: NSObject { var name: String var isMale: Bool var age: Int init(name: String, isMale: Bool, age: Int) { self.name = name self.isMale = isMale self.age = age super.init() } } 实例内存布局: 偏移量 内容 大小 说明 0x0-0x7 isa_t isa 8字节 指向类对象,包含优化的位域信息 0x8-0x17 String name 16字节 Swift String结构(64位系统) 0x18 Bool isMale 1字节 布尔值 0x19-0x1F (padding) 7字节 Swift编译器根据下一个字段(Int,8字节对齐要求)自动插入填充,确保Int字段从8的倍数地址开始 0x20-0x27 Int age 8字节 64位整数 0x28-0x2F (padding) 8字节 最终内存对齐填充 关键特性: ...

May 6, 2026