Combine 是 Apple 在 WWDC 2019 推出的 响应式编程框架 ,用于在 iOS/macOS/watchOS/tvOS 上处理异步事件流(如网络请求、用户输入、定时器等)。它使用 声明式代码风格,用 Publisher 和 Subscriber 的方式来"组合"异步数据流,从而替代传统的回调、Notification、KVO 等机制。
🧠 一句话理解 Combine
Combine 就是 Apple 官方推出的"响应式数据流处理工具",使用它可以优雅地处理异步逻辑。
🧩 Combine 的核心概念
名称 | 说明 |
---|---|
Publisher | 发布者,表示"可以发出值"的对象(如网络请求、Notification) |
Subscriber | 订阅者,订阅发布者的值并响应处理(如 UI 更新、状态更新) |
Operator | 操作符,用于转换或组合数据流(如 map, filter, merge) |
Subject | 特殊的 Publisher,也能作为数据源主动"发送值" |
Cancellable | 返回值,可用于取消订阅(防止内存泄漏) |
🔨 基本使用示例
1️⃣ 创建 Publisher
csharp
let justPublisher = Just("Hello Combine") // 发出一个值后就完成
2️⃣ 订阅 Publisher
bash
justPublisher.sink { value in
print("接收到值:(value)")
}
🔁 常用操作符(Operators)
操作符 | 说明 |
---|---|
map | 映射值 |
filter | 过滤值 |
combineLatest | 合并多个 Publisher |
flatMap | 链式转换并展开 Publisher |
debounce | 防抖处理,如输入框 |
removeDuplicates | 去重处理 |
catch | 错误捕获并恢复 |
示例:
bash
let numbers = [1, 2, 3, 4, 5].publisher
numbers
.map { $0 * 2 }
.filter { $0 > 5 }
.sink { print("结果:($0)") }
💡 Subject 的使用
PassthroughSubject:用于外部"主动"发送值
dart
let subject = PassthroughSubject<String, Never>()
subject
.sink { print("接收:($0)") }
subject.send("Hello")
subject.send("World")
CurrentValueSubject:有初始值,并能追踪最新状态
bash
let current = CurrentValueSubject<Int, Never>(0)
current
.sink { print("当前值:($0)") }
current.send(10)
🧩 网络请求示例
php
import Combine
import Foundation
struct Post: Decodable {
let title: String
}
var cancellables = Set<AnyCancellable>()
URLSession.shared.dataTaskPublisher(for: URL(string: "https://jsonplaceholder.typicode.com/posts/1")!)
.map(.data)
.decode(type: Post.self, decoder: JSONDecoder())
.sink(receiveCompletion: { completion in
print("完成:(completion)")
}, receiveValue: { post in
print("标题:(post.title)")
})
.store(in: &cancellables)
🧯 如何取消订阅(防止内存泄漏)
scss
let cancellable = Just("Hello").sink { print($0) }
cancellable.cancel()
使用 .store(in: &cancellables) 自动释放:
scss
var cancellables = Set<AnyCancellable>()
Just("Hello")
.sink { print($0) }
.store(in: &cancellables)
🧠 Combine 与 UIKit 结合示例
监听UITextField输入变化
swift
import Combine
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var textField: UITextField!
var cancellables = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: textField)
.compactMap { ($0.object as? UITextField)?.text }
.debounce(for: .milliseconds(300), scheduler: RunLoop.main)
.sink { text in
print("输入:(text)")
}
.store(in: &cancellables)
}
}
🆚 与 RxSwift 区别
项目 | Combine | RxSwift |
---|---|---|
是否官方 | ✅ Apple 官方 | ❌ 三方库(强大但外部) |
支持平台 | iOS 13+ | iOS 9+ |
学习曲线 | 相对较陡 | 相对陡(语法更复杂) |
文档与社区 | 官方文档丰富 | 社区丰富 |
✅ Combine 使用建议
- iOS 13+ 原生项目推荐使用 Combine;
- UI 绑定推荐使用 @Published + ObservableObject;
- 更复杂的流处理可以结合 Subject 和 Operator;
- 异步任务建议统一封装成 Publisher 进行管理。