我使用RxCocoa和RxSwift来渲染UITableView
对由BehaviorRelay
提供的数组。绑定数据的代码如下:
// MyViewModel:
var itemList = BehaviorRelay(value: [MyItem]())
...
func loadData() {
var items = [...] // load items
itemList.accept(items)
}
// ==========
// MyView:
var viewModel = MyViewModel()
func bindData() {
viewModel.itemList.bind(to: tableView.rx.items) { table, index, item in
if item.type == ... {
... // pick cell type based on the item metadata
} else {
let cell = table.dequeueReusableCell(withIdentifier: "MyItemCell") as? MyItemCell {
cell.bindItem(item)
return cell
}
return new UITableViewCell()
}).disposed(by: viewModel.disposeBag)
}
// ==========
在大多数情况下,它工作得很好,但在一些罕见的情况下,我不能用以下堆栈跟踪重现应用程序崩溃,由几个Firebase使用报告:
Crashed: com.apple.main-thread
0 libswiftCore.dylib 0x37d7c _assertionFailure(_:_:file:line:flags:) + 312
1 mycoolapp 0x8aa968 @objc TableViewDataSourceNotSet.tableView(_:cellForRowAt:) + 84 (RxCocoa.swift:84)
2 mycoolapp 0x8aae30 @objc RxTableViewDataSourceProxy.tableView(_:cellForRowAt:) + 64 (RxTableViewDataSourceProxy.swift:64)
3 UIKitCore 0x14b75c -[UITableView _createPreparedCellForGlobalRow:withIndexPath:willDisplay:] + 808
4 UIKitCore 0x219c9c -[UITableView _createPreparedCellForRowAtIndexPath:willDisplay:] + 68
5 UIKitCore 0x219884 -[UITableView _heightForRowAtIndexPath:] + 124
6 UIKitCore 0x219720 -[UISectionRowData heightForRow:inSection:canGuess:] + 176
7 UIKitCore 0x44c328 -[UITableViewRowData heightForRow:inSection:canGuess:adjustForReorderedRow:] + 228
8 UIKitCore 0x5952c -[UITableViewRowData rectForRow:inSection:heightCanBeGuessed:] + 304
9 UIKitCore 0x44b8b0 -[UITableViewRowData rectForGlobalRow:heightCanBeGuessed:] + 112
10 UIKitCore 0x14b378 -[UITableView _prefetchCellAtGlobalRow:aboveVisibleRange:] + 240
11 UIKitCore 0x14b264 __48-[UITableView _configureCellPrefetchingHandlers]_block_invoke + 52
12 UIKitCore 0x2186a8 -[_UITableViewPrefetchContext updateVisibleIndexRange:withContentOffset:] + 2100
13 UIKitCore 0x81be8 -[UITableView _updateCycleIdleUntil:] + 168
14 UIKitCore 0x811ac ___UIUpdateCycleNotifyIdle_block_invoke + 612
15 libdispatch.dylib 0x2460 _dispatch_call_block_and_release + 32
16 libdispatch.dylib 0x3f88 _dispatch_client_callout + 20
17 libdispatch.dylib 0x127f4 _dispatch_main_queue_drain + 928
18 libdispatch.dylib 0x12444 _dispatch_main_queue_callback_4CF + 44
19 CoreFoundation 0x9a6c8 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 16
20 CoreFoundation 0x7c02c __CFRunLoopRun + 2036
21 CoreFoundation 0x80eb0 CFRunLoopRunSpecific + 612
22 GraphicsServices 0x1368 GSEventRunModal + 164
23 UIKitCore 0x3a1668 -[UIApplication _run] + 888
24 UIKitCore 0x3a12cc UIApplicationMain + 340
25 mycoolapp. 0x1d8564 main + 23 (AppDelegate.swift:23)
26 ??? 0x1b3d9c960 (Missing)
崩溃很难重现(我不能),但它影响了多个用户。bindData
方法在viewDidLoad
被调用一次,我在每个页面外观上调用loadData
(将出现,从另一个页面或应用程序激活停用返回),但不清楚为什么它崩溃以及如何复制/修复它。
崩溃来自TableViewDataSourceNotSet
,这似乎是由RxCocoa/RxSwift框架设置的,当请求一个项目的单元格时崩溃。我没有在任何地方显式设置自定义数据源。有什么办法可以防止这种情况发生吗?
bindData方法在每个页面外观(willAppear)上调用,但不清楚为什么它崩溃以及如何复制/修复它。
以上是一个危险信号。您不应该在每个页面外观上都进行绑定。导致解除绑定然后重新绑定的最佳情况。这可能导致系统在某些地方出错,表视图从先前安装的数据源调用numberOfRowsInSection
,然后在TableViewDataSourceNotSet
对象上调用cellForRowAt
。
你的绑定应该只发生一次。通常在viewDidLoad。