You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

咨询RxSwift或Combine中替代自定义可变可观察对象的方案

RxSwift 解决方案

1. BehaviorRelay 标准用法

BehaviorRelay 确实只能通过 accept(_:) 传入新数组,但可以通过先获取当前值、修改后再发送的方式实现内部元素的增删改:

// ViewModel 中
let itemsRelay = BehaviorRelay<[String]>(value: [])

// 添加元素
func addItem(_ item: String) {
    var currentItems = itemsRelay.value
    currentItems.append(item)
    itemsRelay.accept(currentItems)
}

// 删除指定索引的元素
func removeItem(at index: Int) {
    var currentItems = itemsRelay.value
    guard index < currentItems.count else { return }
    currentItems.remove(at: index)
    itemsRelay.accept(currentItems)
}

在 Controller 中订阅并绑定到 TableView:

viewModel.itemsRelay
    .bind(to: tableView.rx.items(cellIdentifier: "Cell")) { index, item, cell in
        cell.textLabel?.text = item
    }
    .disposed(by: disposeBag)

2. RxDataSources(高效局部刷新)

如果想避免全量刷新、只更新变化的行,推荐用 RxDataSources,它能自动计算数组差异并生成对应 TableView 更新事件:

// 定义 Section 模型
struct ItemSection {
    var items: [String]
}

extension ItemSection: SectionModelType {
    typealias Item = String
    
    init(original: ItemSection, items: [String]) {
        self = original
        self.items = items
    }
}

// ViewModel 中
let sectionsRelay = BehaviorRelay<[ItemSection]>(value: [ItemSection(items: [])])

// Controller 中配置数据源
let dataSource = RxTableViewSectionedAnimatedDataSource<ItemSection>(
    configureCell: { _, tableView, indexPath, item in
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)!
        cell.textLabel?.text = item
        return cell
    }
)

viewModel.sectionsRelay
    .bind(to: tableView.rx.items(dataSource: dataSource))
    .disposed(by: disposeBag)

修改数组时仍按“取当前值→修改→发送”的流程操作,RxDataSources 会自动处理局部刷新。

Combine 解决方案

1. @Published 属性(简洁实现)

直接用 @Published 修饰数组属性,数组变化时会自动发送事件:

// ViewModel 中
class MyViewModel: ObservableObject {
    @Published var items: [String] = []
    
    func addItem(_ item: String) {
        items.append(item) // 直接修改数组,@Published 自动发送通知
    }
    
    func removeItem(at index: Int) {
        guard index < items.count else { return }
        items.remove(at: index)
    }
}

在 Controller 中持有 ViewModel 并订阅刷新:

class MyViewController: UIViewController {
    @ObservedObject var viewModel = MyViewModel()
    private var cancellables = Set<AnyCancellable>()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        viewModel.$items
            .receive(on: DispatchQueue.main)
            .sink { [weak self] _ in
                self?.tableView.reloadData()
            }
            .store(in: &cancellables)
    }
}

2. CurrentValueSubject(灵活手动控制)

如果需要更灵活的事件发送控制,可用 CurrentValueSubject,用法和 BehaviorRelay 类似:

// ViewModel 中
class MyViewModel {
    let itemsSubject = CurrentValueSubject<[String], Never>([])
    
    func addItem(_ item: String) {
        var currentItems = itemsSubject.value
        currentItems.append(item)
        itemsSubject.send(currentItems)
    }
}

Controller 中直接订阅 itemsSubject 即可监听数组变化。

3. Diffable DataSource + Combine(高效局部刷新)

Combine 可配合 UIKit 的 UITableViewDiffableDataSource 实现高效局部刷新,无需手动调用 reloadData():

// ViewModel 中
class MyViewModel: ObservableObject {
    @Published var items: [String] = []
}

// Controller 中
class MyViewController: UIViewController {
    @ObservedObject var viewModel = MyViewModel()
    private var dataSource: UITableViewDiffableDataSource<Int, String>!
    private var cancellables = Set<AnyCancellable>()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 配置 Diffable DataSource
        dataSource = UITableViewDiffableDataSource(tableView: tableView) { tableView, indexPath, item in
            let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)!
            cell.textLabel?.text = item
            return cell
        }
        
        // 订阅数组变化并更新快照
        viewModel.$items
            .receive(on: DispatchQueue.main)
            .sink { [weak self] items in
                var snapshot = NSDiffableDataSourceSnapshot<Int, String>()
                snapshot.appendSections([0])
                snapshot.appendItems(items)
                self?.dataSource.apply(snapshot)
            }
            .store(in: &cancellables)
    }
}

内容的提问来源于stack exchange,提问作者Sevenfortyseven

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 18:01:05