Combine 基本使用指南

Combine 基本使用指南

Combine 是 Apple 在 2019 年推出的响应式编程框架,用于处理随时间变化的值流。下面是 Combine 的基本概念和使用方法。

核心概念

1. Publisher(发布者)

  • 产生值的源头
  • 可以发送 0 个或多个值
  • 可能以完成或错误结束

2. Subscriber(订阅者)

  • 接收来自 Publisher 的值
  • 可以控制数据流的需求

3. Operator(操作符)

  • 转换、过滤、组合来自 Publisher 的值

基本使用示例

创建简单的 Publisher

swift 复制代码
import Combine

// 1. Just - 发送单个值然后完成
let justPublisher = Just("Hello, World!")

// 2. Sequence - 发送序列中的值
let sequencePublisher = [1, 2, 3, 4, 5].publisher

// 3. Future - 异步操作的结果
func fetchData() -> Future<String, Error> {
    return Future { promise in
        // 模拟异步操作
        DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
            promise(.success("Data fetched"))
        }
    }
}

// 4. @Published 属性包装器
class DataModel {
    @Published var name: String = "Initial"
}

订阅 Publisher

swift 复制代码
// 使用 sink 订阅
var cancellables = Set<AnyCancellable>()

// 订阅 Just
justPublisher
    .sink { value in
        print("Received value: \(value)")
    }
    .store(in: &cancellables)

// 订阅 Sequence
sequencePublisher
    .sink(
        receiveCompletion: { completion in
            switch completion {
            case .finished:
                print("Finished successfully")
            case .failure(let error):
                print("Failed with error: \(error)")
            }
        },
        receiveValue: { value in
            print("Received: \(value)")
        }
    )
    .store(in: &cancellables)

常用操作符

swift 复制代码
// 转换操作符
sequencePublisher
    .map { $0 * 2 }                    // 转换每个值
    .filter { $0 > 5 }                 // 过滤值
    .reduce(0, +)                      // 聚合值
    .sink { print("Result: \($0)") }
    .store(in: &cancellables)

// 组合操作符
let publisher1 = [1, 2, 3].publisher
let publisher2 = ["A", "B", "C"].publisher

Publishers.Zip(publisher1, publisher2)
    .sink { print("Zipped: \($0), \($1)") }
    .store(in: &cancellables)

// 错误处理
enum MyError: Error {
    case testError
}

Fail(outputType: String.self, failure: MyError.testError)
    .catch { error in
        Just("Recovered from error")
    }
    .sink { print($0) }
    .store(in: &cancellables)

处理 UI 更新

swift 复制代码
import UIKit
import Combine

class ViewController: UIViewController {
    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var textField: UITextField!
    @IBOutlet weak var button: UIButton!
    
    private var cancellables = Set<AnyCancellable>()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupBindings()
    }
    
    private func setupBindings() {
        // 监听文本框变化
        NotificationCenter.default
            .publisher(for: UITextField.textDidChangeNotification, object: textField)
            .compactMap { ($0.object as? UITextField)?.text }
            .sink { [weak self] text in
                self?.label.text = "You typed: \(text)"
            }
            .store(in: &cancellables)
        
        // 按钮点击事件
        button.publisher(for: .touchUpInside)
            .sink { [weak self] _ in
                self?.handleButtonTap()
            }
            .store(in: &cancellables)
    }
    
    private func handleButtonTap() {
        print("Button tapped!")
    }
}

网络请求示例

swift 复制代码
struct User: Codable {
    let id: Int
    let name: String
    let email: String
}

class UserService {
    private var cancellables = Set<AnyCancellable>()
    
    func fetchUsers() -> AnyPublisher<[User], Error> {
        guard let url = URL(string: "https://jsonplaceholder.typicode.com/users") else {
            return Fail(error: URLError(.badURL))
                .eraseToAnyPublisher()
        }
        
        return URLSession.shared.dataTaskPublisher(for: url)
            .map(\.data)
            .decode(type: [User].self, decoder: JSONDecoder())
            .eraseToAnyPublisher()
    }
    
    func loadUsers() {
        fetchUsers()
            .receive(on: DispatchQueue.main) // 切换到主线程
            .sink(
                receiveCompletion: { completion in
                    switch completion {
                    case .finished:
                        print("Request completed")
                    case .failure(let error):
                        print("Error: \(error)")
                    }
                },
                receiveValue: { users in
                    print("Received users: \(users)")
                }
            )
            .store(in: &cancellables)
    }
}

定时器示例

swift 复制代码
class TimerExample {
    private var cancellables = Set<AnyCancellable>()
    
    func startTimer() {
        Timer.publish(every: 1.0, on: .main, in: .common)
            .autoconnect()
            .sink { [weak self] date in
                print("Timer fired at: \(date)")
                self?.handleTimerTick()
            }
            .store(in: &cancellables)
    }
    
    private func handleTimerTick() {
        // 处理定时器触发
    }
}

内存管理

swift 复制代码
class MyViewController: UIViewController {
    private var cancellables = Set<AnyCancellable>()
    
    deinit {
        // 自动取消所有订阅
        cancellables.forEach { $0.cancel() }
    }
}

最佳实践

  1. 及时取消订阅 :使用 store(in:) 管理订阅生命周期
  2. 线程切换 :使用 receive(on:) 在合适的线程处理数据
  3. 错误处理 :合理使用 catchreplaceError 等操作符
  4. 避免强引用循环 :在闭包中使用 [weak self]

这些是 Combine 的基本使用方法。Combine 提供了强大的响应式编程能力,特别适合处理异步事件流和数据绑定。

相关推荐
芳草萋萋鹦鹉洲哦37 分钟前
【tauri+pixijs】关于unicode/ascII/GB2312
前端·tauri·pixijs
木易 士心44 分钟前
th-table 中 基于双字段计算的表格列展示方案
前端·javascript·angular.js
fakaifa1 小时前
【全开源】智慧共享农场源码独立版+uniapp前端
前端·uni-app·智慧农场·源码下载·智慧农场小程序·智慧共享农场
toooooop82 小时前
uniapp多个页面监听?全局监听uni.$emit/$on
前端·javascript·uni-app
骨子里的偏爱2 小时前
【案例】uniapp实现内部信息与外部的html网页双向通信的完整的过程,附加完整的代码部分
前端·uni-app·html
爱泡脚的鸡腿2 小时前
uni-app D4 实战(小兔鲜)
前端·vue.js·架构
星火飞码iFlyCode2 小时前
iFlyCode+SpecKit应用:照片等比智能压缩功能实现
前端·javascript
广白2 小时前
钉钉小程序直传文件到 阿里云OSS
前端·vue.js·uni-app
zyfts3 小时前
🔥告别 20 分钟等待!NestJS 生产级消息队列 BullMQ 实践指南
前端·后端
狗头大军之江苏分军3 小时前
【压力】一位一线炼钢工人的消失
前端·后端