Combine的介绍与使用
目录
- 一、Combine 框架介绍
- 二、核心概念
- 三、基础使用示例
- 3.1、创建 Publisher & 订阅
- 3.2、操作符链式调用
- 3.3、Subject 使用(手动发送值)
- 3.4、网络请求处理
- 3.5、组合多个 Publisher
- 3.6、错误处理
- 四、核心操作符速查表 Operator
- 五、UIKit 绑定示例
- 六、SwiftUI 集成示例
- 七、最佳实践建议
一、Combine 框架介绍
Combine 是 Apple 在 2019 年推出的声明式响应式编程框架,专为 Swift 设计,深度集成在 Apple 生态系统中(iOS 13+,macOS 10.15+)。它提供了一套统一的 API 来处理随时间变化的数据流,特别适合处理异步事件、数据绑定和复杂的状态管理。
二、核心概念
- Publisher:数据生产者(发布事件)
- Subscriber:数据消费者(接收事件)
- Operator:数据转换操作符
- Subject:可手动发送值的特殊 Publisher
- Scheduler:线程调度管理器
三、基础使用示例
3.1、创建 Publisher & 订阅
import Combinevar cancellables = Set<AnyCancellable>()// 创建 Publisher
let stringPublisher = Just("Hello Combine!") // 发送单个值
let arrayPublisher = [1, 2, 3].publisher // 发送序列值// 订阅 Publisher
stringPublisher.sink(receiveCompletion: { completion inswitch completion {case .finished: print("Completed")case .failure(let error): print("Error: \(error)")}},receiveValue: { value inprint(value) // "Hello Combine!"}).store(in: &cancellables) // 存储订阅
3.2、操作符链式调用
[1, 2, 3, 4, 5].publisher.filter { $0 % 2 == 0 } // 过滤偶数.map { $0 * 10 } // 转换值.sink { print($0) } // 接收值.store(in: &cancellables)/* 输出:
20
40
*/
3.3、Subject 使用(手动发送值)
// PassthroughSubject:不保存当前值
let passSubject = PassthroughSubject<String, Never>()// CurrentValueSubject:保存当前值
let currentSubject = CurrentValueSubject<Int, Never>(0)// 订阅
passSubject.sink { print("收到: \($0)") }.store(in: &cancellables)// 发送值
passSubject.send("第一次发送")
passSubject.send("第二次发送")// 修改当前值
currentSubject.value = 5
currentSubject.send(10) // 发送新值
3.4、网络请求处理
struct User: Decodable {let name: String
}func fetchUser() -> AnyPublisher<User, Error> {let url = URL(string: "https://api.example.com/user")!return URLSession.shared.dataTaskPublisher(for: url).map(\.data) // 提取数据.decode(type: User.self, decoder: JSONDecoder()) // 解码.receive(on: DispatchQueue.main) // 切换到主线程.eraseToAnyPublisher() // 类型擦除
}// 使用
fetchUser().sink(receiveCompletion: { print($0) },receiveValue: { user inprint("用户名: \(user.name)")}).store(in: &cancellables)
3.5、组合多个 Publisher
let usernamePublisher = PassthroughSubject<String, Never>()
let passwordPublisher = PassthroughSubject<String, Never>()// 组合最新值
Publishers.CombineLatest(usernamePublisher, passwordPublisher).map { username, password in!username.isEmpty && password.count >= 6}.sink { isValid inprint("表单有效? \(isValid)")}.store(in: &cancellables)// 触发验证
usernamePublisher.send("user")
passwordPublisher.send("123") // 输出: false
passwordPublisher.send("123456") // 输出: true
3.6、错误处理
enum CustomError: Error { case test }Fail(error: CustomError.test) // 立即发送错误的Publisher.catch { error -> Just<String> inprint("捕获错误: \(error)")return Just("默认值")}.sink(receiveValue: { print($0) }).store(in: &cancellables)/* 输出:
捕获错误: test
默认值
*/
四、核心操作符速查表 Operator
操作符 | 功能描述 |
---|---|
map | 值转换 |
filter | 条件过滤 |
flatMap | 展平嵌套Publisher |
combineLatest | 组合多个Publisher的最新值 |
merge | 合并多个Publisher |
debounce | 防抖动(用于搜索输入) |
throttle | 节流(控制事件频率) |
retry | 失败重试 |
switchToLatest | 切换到最新Publisher |
五、UIKit 绑定示例
class ViewController: UIViewController {@IBOutlet weak var textField: UITextField!@IBOutlet weak var label: UILabel!private var cancellables = Set<AnyCancellable>()override func viewDidLoad() {super.viewDidLoad()// 创建文本框内容Publisherlet textPublisher = NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: textField).compactMap { ($0.object as? UITextField)?.text }// 绑定到标签textPublisher.filter { $0.count > 3 }.map { "输入: \($0)" }.assign(to: \.text, on: label).store(in: &cancellables)}
}
六、SwiftUI 集成示例
import SwiftUI
import Combinestruct ContentView: View {@StateObject private var viewModel = ViewModel()var body: some View {VStack {TextField("搜索", text: $viewModel.searchText)List(viewModel.results, id: \.self) { item inText(item)}}}
}class ViewModel: ObservableObject {@Published var searchText = ""@Published var results: [String] = []private var cancellables = Set<AnyCancellable>()init() {$searchText.debounce(for: .seconds(0.5), scheduler: RunLoop.main).removeDuplicates().flatMap { query -> AnyPublisher<[String], Never> inself.search(query: query)}.assign(to: \.results, on: self).store(in: &cancellables)}func search(query: String) -> AnyPublisher<[String], Never> {// 模拟网络请求let results = query.isEmpty ? [] : ["\(query)结果1", "\(query)结果2"]return Just(results).eraseToAnyPublisher()}
}
七、最佳实践建议
- 内存管理:始终使用
store(in: &cancellables)
管理订阅生命周期 - 线程切换:使用
receive(on:)
确保在正确线程更新 UI - 错误处理:合理使用
catch
,retry
等操作符 - 避免强引用:使用
[weak self]
防止循环引用 - 调试技巧:使用
print()
操作符跟踪事件流
注意:Combine 要求最低系统版本 iOS 13+/macOS 10.15+。对于支持旧系统的项目,可考虑使用 RxSwift 或其他响应式框架。