咨询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
相关产品推荐
相关产品推荐

