我在一个应用程序组件中有一个简单的信号,它返回一个项目数组:
var itemsSignal: Signal<[Item], Never>
这些项目可能包含以表格 View 形式呈现在屏幕上的数据的更新。任务是将更新应用到屏幕上出现的单元格。
我能想到有两种可能的方法来实现这一点。该应用程序是用 MVVM 风格编写的,但我只是为了示例的目的。
第一种方法是在 View Controller 代码级别上订阅此信号一次,然后在 observeValues
block 中检查我们收到的项目更新的位置使用一些 for 循环进行屏幕并更新相应单元格的状态。这样,我们将只有一个订阅,但在我看来,当我们基本上使用 View Controller 级别代码将更新从源传递到屏幕上的各个单元格时,这会引入不必要的代码耦合。
第二种方法是从每个单独的单元(在现实单元的 View 模型中)订阅此信号并应用一些过滤,如下所示:
disposables += COMPONENT.itemsSignal
.flatten()
.filter({ $0.itemId == itemId })
.observeValues({
...
})
但这会创建多个订阅 - 每个单元对应一个订阅。
我实际上更喜欢第二种方法,因为在我看来,从设计的角度来看它更干净,因为它不会泄漏任何不必要的知识来查看 Controller 级别代码。无论何时在不同的屏幕上重复使用相同的单元格,这种 self 更新行为都将被继承并且开箱即用。
问题是,由于多个订阅,第二种方法的内存/CPU 成本要高出多少?在此项目中,我们使用 ReactiveSwift
,但我认为这也与其他 Rx
库相关。
最佳答案
在 RxSwift 中,我们的 RxCocoa 库已经实现了您的第一个想法,并在 tableView 上执行了简单的 reloadData。
items
.bind(to: tableView.rx.items) { (tableView, row, element) in
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
cell.textLabel?.text = "\(element) @ row \(row)"
return cell
}
或者您可以告诉库该单元格的类型,它会为您创建单元格:
items
.bind(to: tableView.rx.items(cellIdentifier: "Cell", cellType: UITableViewCell.self)) { (row, element, cell) in
cell.textLabel?.text = "\(element) @ row \(row)"
}
我们还有一个您没有提到的选项。单一订阅,但更智能的数据源,能够根据传入序列元素的相等性添加和删除单个单元格。它位于一个名为 RxDataSources
的单独库中。
作为关于资源的基本问题的答案...我经常使用混合解决方案,其中有一个仅包含对象 ID 序列的 Observable;这是您的第一个想法,但它只负责项目的插入和删除。我将创建第二个由 [ID: Info]
组成的 Observable,每个当前存在的单元格都会订阅该 Observable。在创建/重用单元格时,它会获得一个 ID 并订阅第二个可观察值,并仅过滤掉它感兴趣的信息。在单元格的 prepareForReuse
中,它会取消订阅。
由于每个现有单元格只有一个订阅,因此实际上并没有那么多新订阅(取决于单元格的高度和表格 View 的高度)。我的应用程序通常有数千个订阅任何一次运行的订阅数量,因此通过这种“每单元格”方法添加的额外订阅数量甚至不明显。
关于ios - ReactiveSwift 一个信号订阅与多个信号订阅以及相关的内存开销,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65659620/