目錄
- 一、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 或其他響應式框架。