FoundationModels

2025年WWDC上,Apple把Apple Intelligence背后的那个"设备端基础模型"第一次对开发者开放——这就是FoundationModels框架。过去要在App里接入LLM,基本只有两条路:调OpenAI/Claude这类云端API,或者自己在端上集成llama.cpp、MLC、Core ML等推理框架。前者有隐私和成本问题,后者有工程门槛和包体积问题。FoundationModels试图提供第三条路:一个系统级的、免费的、离线可用的、原生Swift的LLM API。 本文从框架定位开始,一路讲到Guided Generation、Tool Calling、Streaming、Adapter微调等细节,目标是让你在读完后能直接上手用它写出一个生产可用的功能。 一、FoundationModels是什么 1.1 一句话定义 FoundationModels是iOS 26 / macOS 26及之后的系统框架,提供对设备端Apple Intelligence语言模型的编程访问能力。模型本身约3B参数,经过4-bit量化,常驻系统,不计入App包体积,所有推理在设备上完成。 1.2 能做什么 / 不能做什么 能做 不能做 文本生成、摘要、分类、抽取、改写、翻译 世界知识问答(模型太小,不适合当"百科") 结构化输出(生成指定Swift类型) 图像生成 / 视频生成 工具调用(Function Calling) 嵌入向量生成(Embedding) 短对话、小规模推理 长上下文的复杂推理(窗口有限) 完全离线、无需联网 替代GPT-4这类前沿大模型 定位非常清楚:它是一个"端侧小而美"的模型,负责那些没必要上云、对延迟/隐私/成本敏感的任务。 1.3 为什么值得iOS开发者关注 零成本:不像调OpenAI API,每token都花钱。 零延迟网络层:首token延迟可以做到100ms级。 隐私合规:输入不出设备,符合医疗、金融、教育场景的合规要求。 离线可用:地铁、飞机、隧道里也能跑。 原生Swift DSL:宏驱动的@Generable、Tool协议,跟SwiftUI一样"声明式"。 与系统深度集成:可直接访问Apple Intelligence上下文(比如屏幕识别、个人数据等,受权限约束)。 二、整体架构 graph TB subgraph APP ["应用层"] UI["SwiftUI / UIKit"] VM["ViewModel"] end subgraph FM ["FoundationModels Framework"] SLM["SystemLanguageModel模型句柄 + 可用性"] SESSION["LanguageModelSession会话对象 / 上下文"] GEN["Generable宏结构化输出"] TOOL["Tool协议工具调用"] STREAM["ResponseStream流式输出"] GUARD["Guardrails安全过滤"] end subgraph SYSTEM ["系统层"] ANE["Apple Neural Engine"] GPU["GPU"] MODEL["On-device Foundation Model≈3B params, 4-bit quant"] ADAPTER["LoRA Adapters"] end UI --> VM VM --> SESSION SESSION --> SLM SESSION --> GEN SESSION --> TOOL SESSION --> STREAM SESSION --> GUARD SLM --> MODEL MODEL --> ANE MODEL --> GPU ADAPTER --> MODEL 几个关键抽象: ...

May 2, 2026

命令模式

定义 命令模式(Command Pattern)是一种行为型设计模式,它将请求封装成对象,从而可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。 命令模式的核心思想是:将"请求"封装成一个对象,使得可以用不同的请求、队列或日志来参数化其他对象,同时支持可撤销操作。 为什么需要命令模式 命令模式要解决的核心问题是:将请求(操作)封装成对象,使得可以对请求进行存储、传递、撤销等操作。 问题场景:假设我们正在开发一个文本编辑器,需要支持撤销/重做功能。 最直接的方式可能是这样: class TextEditor { var text: String = "" func insertText(_ newText: String, at position: Int) { let index = text.index(text.startIndex, offsetBy: position) text.insert(contentsOf: newText, at: index) } func deleteText(from start: Int, length: Int) { let startIndex = text.index(text.startIndex, offsetBy: start) let endIndex = text.index(startIndex, offsetBy: length) text.removeSubrange(startIndex..<endIndex) } } // 用户操作 let editor = TextEditor() editor.insertText("Hello", at: 0) editor.insertText(" World", at: 5) editor.deleteText(from: 5, length: 6) // 如何实现撤销?需要记住每次操作的类型、参数、以及如何逆向执行 // 这变得非常复杂... 这种方式有什么问题? ...

May 2, 2026

WebView底层原理

前言 iOS 上的 WebView 目前以 WKWebView 为事实标准。它对外表现得像一个 UIView 子类,但内部其实对接着整个 WebKit 引擎——一个由多个独立进程协作的庞大系统。日常开发中遇到的那些困扰:为什么 Web 页白屏不会把 App 带崩?为什么 Cookie 和 App 自身的 NSHTTPCookieStorage 总是对不上?为什么 JS 调用 Native 永远是异步?为什么 NSURLProtocol 拦不到 WKWebView 的请求?这些问题单看 API 都得不到答案,必须下潜到 WebKit 源码里才能看清楚。 好在 WebKit 是 Apple 官方开源的项目,仓库在 github.com/WebKit/WebKit。本文基于其 Source/ 目录下的公开源码,按"源码地图 → 多进程架构 → 各进程职责 → 全链路串联 → 工程启示"的顺序,把 WKWebView 从创建到渲染一帧的底层机制梳理清楚。 一、先看源码地图 阅读源码前,先在脑子里建立目录结构。以 WebKit/WebKit 仓库的 Source/ 目录为例: Source/ ├── JavaScriptCore/ # JS 引擎(解释器、JIT 编译器、GC) ├── WebCore/ # 渲染引擎核心(DOM、CSSOM、Layout、Paint) ├── WebKit/ # 多进程框架 + Cocoa 层 API │ ├── UIProcess/ # 主进程侧(App 进程) │ │ ├── API/Cocoa/ # WKWebView.mm、WKProcessPool.mm 等 │ │ ├── API/ios/ # WKWebViewIOS.mm │ │ ├── ios/ # WKContentView.mm、WebPageProxyIOS.mm │ │ └── ... │ ├── WebProcess/ # 渲染进程侧(WebContent) │ ├── NetworkProcess/ # 独立网络进程 │ ├── GPUProcess/ # 独立 GPU 进程 │ └── Shared/ # 跨进程共享的数据结构、IPC 消息定义 └── WTF/ # 基础容器、线程、字符串工具库 日常 iOS 开发能看到的 WKWebView,对应的实现就在 Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm。它是一个 Objective-C++ 文件,内部只是持有一个 C++ 的 WebPageProxy 对象,几乎所有实际工作都转发给它。为什么要这样设计?原因就藏在下一节要讲的多进程架构里。 ...

May 2, 2026

SOLID原则

概述 SOLID 是面向对象设计的五大基本原则的首字母缩写,由 Robert C. Martin(Uncle Bob)在 2000 年代初期提出。这五个原则是构建可维护、可扩展软件的基础,在 iOS 开发中同样适用。 字母 原则 英文名称 S 单一职责原则 Single Responsibility Principle O 开放封闭原则 Open-Closed Principle L 里氏替换原则 Liskov Substitution Principle I 接口隔离原则 Interface Segregation Principle D 依赖倒置原则 Dependency Inversion Principle 单一职责原则(Single Responsibility Principle, SRP) 定义 A class should have one, and only one, reason to change. 一个类应该只有一个引起它变化的原因。 原则解读 单一职责原则要求每个类只负责一项职责。这里的"职责"可以理解为"变化的原因"——如果你能想到多于一个的原因去改变一个类,那么这个类就有多于一个的职责。 核心思想:将不同的关注点分离,使每个模块只专注于做好一件事。 解决的问题 1. 代码耦合度高 当一个类承担多个职责时,这些职责会相互依赖、相互影响。修改其中一个职责的代码,可能会无意中破坏另一个职责的功能。 2. 难以复用 如果一个类混合了多个职责,当你只想使用其中一个职责时,不得不引入整个类及其所有依赖。 3. 维护困难 职责混杂的类通常代码量大、逻辑复杂,理解和修改都很困难。不同职责的代码交织在一起,修改一处可能产生意想不到的连锁反应。 4. 测试复杂 ...

May 2, 2026

React Native 详解

React Native 是 Meta 2015 年开源的跨平台框架,用 JavaScript/TypeScript 写一份业务代码,在 iOS 和 Android 上渲染出真正的原生控件(UIView / android.view.View),而不是像 Flutter 那样自绘,也不是像 H5 那样跑在 WebView 里。经过 2018~2024 年的"新架构"大重构,RN 在 0.76 让 New Architecture(Fabric + TurboModules + JSI + Codegen + Bridgeless)成为默认,并在 0.82(2025 年 10 月)彻底告别旧的 Bridge 时代——这是 RN 十年来最大的一次范式迁移。 对 iOS 开发者而言,RN 的定位很特殊:UI 仍然是 UIKit,但控制 UI 的大脑换成了 JS。这让它一方面拥有"原生 Look & Feel“的天然优势(iOS 26 Liquid Glass 不用等框架跟进),另一方面又背负着”JS/原生边界抖动“的原罪。在 AI Coding 时代,RN 凭借 Web 生态庞大的训练语料和 Expo 工具链成为 LLM 生成代码正确率最高的移动框架,但 JSI 的 C++ 层和 Bridgeless 的新调试链路也让"AI 修 Bug"变得比以前更难。 ...

May 2, 2026

单例模式

定义 单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。 单例模式的核心要点: 唯一性:整个应用程序生命周期内只存在一个实例 全局访问:提供一个全局访问点获取该实例 懒加载:通常在第一次使用时才创建实例 为什么需要单例模式 单例模式要解决的核心问题是:确保某个类在整个应用中只有一个实例,并提供全局访问点。 问题场景:假设我们正在开发一个App,需要管理用户的登录状态和用户信息。 如果不使用单例,可能会遇到这样的问题: class UserManager { var currentUser: User? var isLoggedIn: Bool = false func login(user: User) { currentUser = user isLoggedIn = true } } // 在登录页面 let userManager1 = UserManager() userManager1.login(user: user) // 在个人中心页面 let userManager2 = UserManager() print(userManager2.isLoggedIn) // false!因为是不同的实例 这种方式有什么问题? 状态不一致:不同地方创建的UserManager是不同的实例,它们的状态相互独立,导致登录状态无法共享 资源浪费:如果UserManager管理的是数据库连接、网络会话等资源,创建多个实例会浪费系统资源 数据同步困难:多个实例之间的数据需要手动同步,容易出错 单例模式的解决思路: 单例模式确保整个应用只有一个UserManager实例,所有地方都访问同一个对象: class UserManager { static let shared = UserManager() private init() {} var currentUser: User? var isLoggedIn: Bool = false func login(user: User) { currentUser = user isLoggedIn = true } } // 在登录页面 UserManager.shared.login(user: user) // 在个人中心页面 print(UserManager.shared.isLoggedIn) // true!同一个实例 适合使用单例的场景: ...

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

KMP(Kotlin Multiplatform)详解

KMP(Kotlin Multiplatform)是 JetBrains 推出的跨平台代码共享方案。它与 Flutter、React Native 的"一套代码一套 UI"思路不同,走的是"业务逻辑共享,UI 原生“的路线:Kotlin 写的网络、数据库、ViewModel、业务规则等代码编译成 iOS/Android/HarmonyOS/Web 可直接使用的二进制产物,而 UI 仍然由各平台原生框架(SwiftUI/UIKit、Jetpack Compose、ArkUI、DOM/Compose Web)自己实现。2023 年 11 月 KMP 正式 Stable;2024 年 6 月华为开发者大会(HDC 2024)上 JetBrains 与华为合作宣布 Kotlin 原生支持 HarmonyOS NEXT,KMP 正式扩展到"Android + iOS + 鸿蒙 + Web"的四端矩阵;随后 Compose Multiplatform for iOS 经历 Alpha → Beta 演进,使得"连 UI 也可以一起共享"成为可选项。 对 iOS 开发者而言,KMP 的意义在于:Android/鸿蒙同事写的 Kotlin 代码你可以像 Swift 一样调用,而代价只是多一个 XCFramework 和一点桥接代码。本文从架构开始,一路讲到编译原理、内存模型、iOS/鸿蒙互操作细节与工程最佳实践。 一、KMP 是什么 1.1 一句话定义 KMP 是 Kotlin 官方的跨平台编译工具链,允许你用 Kotlin 写一份"业务逻辑"代码,编译到多个目标平台: ...

May 2, 2026

KISS原则

什么是KISS原则? KISS(Keep It Simple, Stupid)原则是软件工程中最重要的设计原则之一。它的核心理念是:简单的解决方案比复杂的解决方案更好。 核心思想 简单不等于简陋 KISS 原则强调的"简单"是指: 易于理解:代码的意图一目了然 易于修改:改动时不会牵一发而动全身 易于调试:出问题时容易定位原因 易于测试:测试用例简单直接 简单不意味着功能缺失或代码草率,而是用最直接的方式解决问题。 复杂性的代价 不必要的复杂性会带来: 更高的学习成本:新成员需要更长时间理解代码 更多的 bug:复杂逻辑更容易出错 更难维护:修改一处可能影响多处 更慢的开发速度:需要考虑更多的边界情况 graph LR A[复杂性增加] --> B[理解成本上升] A --> C[Bug 概率上升] A --> D[维护难度上升] B --> E[开发效率下降] C --> E D --> E iOS开发中的KISS实践 1. 选择简单的架构 // 过度设计:小项目使用复杂的 VIPER 架构 // 每个模块都有 View, Interactor, Presenter, Entity, Router // 一个简单的列表页面需要创建 5+ 个文件 // KISS:根据项目规模选择合适的架构 // 小项目:MVC 就足够了 class UserListViewController: UIViewController { private var users: [User] = [] override func viewDidLoad() { super.viewDidLoad() loadUsers() } private func loadUsers() { Task { users = try await UserService.shared.fetchUsers() tableView.reloadData() } } } // 随着项目增长,再逐步引入更复杂的架构 2. 避免过早优化 // 违反 KISS:过早引入复杂的缓存机制 class ImageLoaderOverEngineered { private let memoryCache = NSCache<NSString, UIImage>() private let diskCache: DiskCache private let networkQueue = DispatchQueue(label: "imageLoader", attributes: .concurrent) private let cacheQueue = DispatchQueue(label: "cacheQueue") private var pendingRequests: [URL: [(UIImage?) -> Void]] = [:] private let requestLock = NSLock() func loadImage(from url: URL, completion: @escaping (UIImage?) -> Void) { // 复杂的多级缓存、请求合并、线程同步逻辑... // 对于大多数 App 来说,这可能是过度设计 } } // KISS:从简单开始 class SimpleImageLoader { private let cache = NSCache<NSString, UIImage>() func loadImage(from url: URL) async -> UIImage? { let key = url.absoluteString as NSString // 先检查缓存 if let cached = cache.object(forKey: key) { return cached } // 从网络加载 guard let (data, _) = try? await URLSession.shared.data(from: url), let image = UIImage(data: data) else { return nil } // 存入缓存 cache.setObject(image, forKey: key) return image } } // 当真正遇到性能问题时,再逐步优化 3. 简化条件逻辑 // 违反 KISS:复杂的嵌套条件 func processOrder(_ order: Order) -> Result<Receipt, OrderError> { if order.items.count > 0 { if order.customer != nil { if order.customer!.isVerified { if order.paymentMethod != nil { if order.shippingAddress != nil { if order.totalAmount > 0 { // 终于可以处理订单了... return .success(Receipt()) } else { return .failure(.invalidAmount) } } else { return .failure(.noShippingAddress) } } else { return .failure(.noPaymentMethod) } } else { return .failure(.unverifiedCustomer) } } else { return .failure(.noCustomer) } } else { return .failure(.emptyOrder) } } // KISS:使用 Guard 早期返回 func processOrderSimple(_ order: Order) -> Result<Receipt, OrderError> { guard !order.items.isEmpty else { return .failure(.emptyOrder) } guard let customer = order.customer else { return .failure(.noCustomer) } guard customer.isVerified else { return .failure(.unverifiedCustomer) } guard order.paymentMethod != nil else { return .failure(.noPaymentMethod) } guard order.shippingAddress != nil else { return .failure(.noShippingAddress) } guard order.totalAmount > 0 else { return .failure(.invalidAmount) } return .success(Receipt()) } 4. 使用标准库而非自己实现 // 违反 KISS:自己实现数组去重 func removeDuplicatesManual<T: Hashable>(from array: [T]) -> [T] { var seen = Set<T>() var result = [T]() for element in array { if !seen.contains(element) { seen.insert(element) result.append(element) } } return result } // KISS:使用标准库 let uniqueArray = Array(Set(array)) // 或保持顺序 let uniqueOrderedArray = array.reduce(into: [T]()) { result, element in if !result.contains(element) { result.append(element) } } // 更简单:使用 Swift Algorithms import Algorithms let unique = array.uniqued() 5. 避免过度泛型化 // 违反 KISS:过度泛型,难以理解 protocol DataProviding { associatedtype DataType associatedtype ErrorType: Error func fetch() async throws -> DataType } protocol DataTransforming { associatedtype Input associatedtype Output func transform(_ input: Input) -> Output } class GenericDataLoader< Provider: DataProviding, Transformer: DataTransforming > where Provider.DataType == Transformer.Input { // 复杂的泛型约束... } // KISS:简单直接的实现 class UserLoader { func loadUsers() async throws -> [User] { let data = try await APIClient.shared.fetch("/users") return try JSONDecoder().decode([User].self, from: data) } } // 当真正需要复用时,再考虑抽象 6. 简化 API 设计 // 违反 KISS:过多的配置选项 func createButton( title: String, titleColor: UIColor = .white, titleFont: UIFont = .systemFont(ofSize: 16), backgroundColor: UIColor = .blue, cornerRadius: CGFloat = 8, borderWidth: CGFloat = 0, borderColor: UIColor = .clear, shadowColor: UIColor = .black, shadowOffset: CGSize = .zero, shadowRadius: CGFloat = 0, shadowOpacity: Float = 0, contentInsets: UIEdgeInsets = .init(top: 12, left: 24, bottom: 12, right: 24), isEnabled: Bool = true, // ... 更多参数 ) -> UIButton { // ... } // KISS:提供简单的默认实现和预设样式 enum ButtonStyle { case primary case secondary case destructive case text } func createButton(title: String, style: ButtonStyle = .primary) -> UIButton { let button = UIButton() button.setTitle(title, for: .normal) switch style { case .primary: button.backgroundColor = .systemBlue button.setTitleColor(.white, for: .normal) button.layer.cornerRadius = 8 case .secondary: button.backgroundColor = .systemGray5 button.setTitleColor(.systemBlue, for: .normal) button.layer.cornerRadius = 8 case .destructive: button.backgroundColor = .systemRed button.setTitleColor(.white, for: .normal) button.layer.cornerRadius = 8 case .text: button.backgroundColor = .clear button.setTitleColor(.systemBlue, for: .normal) } return button } // 使用简单 let primaryButton = createButton(title: "Submit") let cancelButton = createButton(title: "Cancel", style: .secondary) 7. 选择合适的数据结构 // 违反 KISS:用复杂的数据结构解决简单问题 class UserStatusManager { private var statusTree: RedBlackTree<String, UserStatus> private var statusIndex: [String: TreeNode<String, UserStatus>] // 复杂的树结构来管理用户状态... } // KISS:简单的字典就够了 class SimpleUserStatusManager { private var userStatus: [String: UserStatus] = [:] func setStatus(_ status: UserStatus, for userId: String) { userStatus[userId] = status } func getStatus(for userId: String) -> UserStatus? { return userStatus[userId] } } 如何平衡简单与功能 渐进式复杂度 遵循"简单开始,按需增长"的原则: ...

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