gpt4 book ai didi

ios - RxSwift 差异库崩溃 `NSInternalInconsistencyException`

转载 作者:行者123 更新时间:2023-11-28 07:19:21 27 4
gpt4 key购买 nike

我现在尝试在一个基本示例中使用 DiffingRxSwift。我正在使用 Differ图书馆。

这是我的 Interactor + ViewModel 关联的:

import Foundation
import RxSwift
import RxCocoa

class Interactor {

var items = [
[1,5,6,7,4,6,7,1,5],
[1,5,2,1,0,6,7],
]

let viewModel: BehaviorRelay<ViewModel>

var currentObjects: Int = 0 {
didSet {
viewModel.accept(.init(with: .loaded(items[currentObjects])))
}
}

init() {
viewModel = BehaviorRelay(value: .init(with: .initialized))
}

func fetchValue() {
currentObjects = currentObjects == 0 ? 1 : 0
}


}

struct ViewModel {

enum ViewModelType: Equatable {
case cell(CellViewModel)
}

enum State {
case initialized
case loaded([Int])
}

let state: State
let viewModels: [ViewModelType]

init(with state: State) {
self.state = state
switch state {
case .initialized: viewModels = []
case .loaded(let values):
viewModels = CellViewModel.from(values).map(ViewModelType.cell)
}
}
}

extension ViewModel: Equatable {

static func ==(left: ViewModel, right: ViewModel) -> Bool {
return left.state == left.state
}
}

extension ViewModel.State: Equatable {

static func ==(left: ViewModel.State, right: ViewModel.State) -> Bool {
switch (left, right) {
case (.initialized, .initialized): return true
case let (.loaded(l), .loaded(r)): return l == r
default: return false
}
}
}

struct CellViewModel {
let description: String
}

extension CellViewModel {

static func from(_ values: [Int]) -> [CellViewModel] {
return values.map { CellViewModel(description: String($0)) }
}
}

extension CellViewModel: Equatable {

static func ==(left: CellViewModel, right: CellViewModel) -> Bool {
return left.description == right.description
}
}

现在对于 View ,我使用一个简单的 `UITableView

import UIKit
import Differ
import RxSwift

class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
...

interactor
.viewModel
.asObservable()
.scan([], accumulator: { (previous, current) in
Array(previous + [current]).suffix(2)
})
.map({ (arr) -> (previous: ViewModel?, current: ViewModel) in
(arr.count > 1 ? arr.first : nil, arr.last!)
}).subscribe(onNext: { [weak self] (previous, current) in
if let prev = previous {
print("Previous => State: \(prev.state) | ViewModelType.count: \(prev.viewModels.count)")
} else {
print("Previous => State: nil | ViewModelType.count: nil")
}
print("Current => State: \(current.state) | ViewModelType.count: \(current.viewModels.count)")
guard let strongSelf = self else { return }
DispatchQueue.main.async {
strongSelf.tableView.animateRowChanges(oldData: previous?.viewModels ?? [], newData: current.viewModels)
}
}).disposed(by: disposeBag)

interactor.fetchValue()
}

@objc
func onRefresh() {
interactor.fetchValue()
}
}

extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return interactor.viewModel.value.viewModels.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellViewModel = interactor.viewModel.value.viewModels[indexPath.row]
switch cellViewModel {
case .cell(let viewModel):
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = viewModel.description
return cell
}
}
}

一切都符合 Equatable,我认为工作会完成,但我得到了一个 NSInternalInconsistencyException 异常。

*** 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“无效更新:第 0 节中的行数无效。更新 (7) 后现有部分中包含的行数必须为等于更新前该部分中包含的行数 (7),加上或减去从该部分插入或删除的行数(插入 7,删除 0),加上或减去移入或移出的行数该部分(0 个搬入,0 个搬出)。'

我在崩溃显示之前检查来自 Rx 的打印件:

Previous => State: nil | ViewModelType.count: nil
Current => State: initialized | ViewModelType.count: 0
Previous => State: initialized | ViewModelType.count: 0
Current => State: loaded([1, 5, 2, 1, 0, 6, 7]) | ViewModelType.count: 7

从逻辑的角度来看,流程对我来说很好。我错过了什么吗?


编辑2019/10/29

我已经在不使用 RxSwift 的情况下制作了另一个版本,以了解问题是否出在 RxSwift 上:

protocol InteractorDelegate: class {
func viewModelDidChange(_ old: ViewModel?, _ new: ViewModel)
}

class Interactor {

weak var delegate: InteractorDelegate?

var items = [
[1,5,6,7,4,6,7,1,5],
[1,5,2,1,0,6,7],
]

var viewModel: ViewModel? {
didSet {
delegate?.viewModelDidChange(oldValue, viewModel!)
}
}

var currentObjects: Int = 0 {
didSet {
viewModel = .init(with: .loaded(items[currentObjects]))
}
}

init() {
viewModel = .init(with: .initialized)
}

func fetchValue() {
currentObjects = currentObjects == 0 ? 1 : 0
}
}

对于 ViewController:

extension ViewController: InteractorDelegate {

func viewModelDidChange(_ old: ViewModel?, _ new: ViewModel) {

if let prev = old {
print("Previous => State: \(prev.state) | ViewModelType.count: \(prev.viewModels.count)")
} else {
print("Previous => State: nil | ViewModelType.count: nil")
}
print("Current => State: \(new.state) | ViewModelType.count: \(new.viewModels.count)")
DispatchQueue.main.async {
self.tableView.animateRowChanges(oldData: old?.viewModels ?? [], newData: new.viewModels)
}
}
}

似乎没有 RxSwift 问题仍然相同:

Previous => State: initialized | ViewModelType.count: 0
Current => State: loaded([1, 5, 2, 1, 0, 6, 7]) | ViewModelType.count: 7
2019-10-29 13:45:56.636678+0900 TestDiffer[93631:21379549] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (7) must be equal to the number of rows contained in that section before the update (7), plus or minus the number of rows inserted or deleted from that section (7 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'

我的 Equatable 一致性有问题吗?


编辑2019/10/29 #2

经过新的测试,我看到只有在之前的值为空时才会发生崩溃。

像这样更改代码后一切正常:

extension ViewController: InteractorDelegate {

func viewModelDidChange(_ old: ViewModel?, _ new: ViewModel) {

DispatchQueue.main.async {
guard old != nil && !old!.viewModels.isEmpty else {
self.tableView.reloadData()
return
}
self.tableView.animateRowChanges(oldData: old!.viewModels, newData: new.viewModels)
}
}
}

放回 RxSwift 而不是委托(delegate)时同样成功。

即使它现在按预期工作。我仍然在质疑为什么当数组为空时 diffing 不起作用。有一个先前的值是空的,一个有 2 个元素的新值应该被分析为 2 个插入,不是吗?这是怎么回事?

最佳答案

如果您实现 RxTableViewDataSourceType 并将其传递给 TableView 的 items 运算符,那就更好了。执行此操作的示例项目位于:https://github.com/danielt1263/RxMultiCounter

我在示例代码中使用了 DifferenceKit,因此您必须在 tableView(_:observedEvent:) 方法中进行一些更改,但这应该有助于为您指明正确的方向。

class RxSimpleAnimatableDataSource<E, Cell>: NSObject, 
RxTableViewDataSourceType,
UITableViewDataSource
where E: Differentiable, Cell: UITableViewCell
{
typealias Element = [E]

init(identifier: String, with animation: UITableView.RowAnimation = .automatic, configure: @escaping (Int, E, Cell) -> Void) {
self.identifier = identifier
self.animation = animation
self.configure = configure
}

func tableView(_ tableView: UITableView, observedEvent: Event<Element>) {
let source = values
let target = observedEvent.element ?? []
let changeset = StagedChangeset(source: source, target: target)
tableView.reload(using: changeset, with: animation) { data in
self.values = data
}
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return values.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath) as! Cell
let row = indexPath.row
configure(row, values[row], cell)
return cell
}

let identifier: String
let animation: UITableView.RowAnimation
let configure: (Int, E, Cell) -> Void
var values: Element = []
}

关于ios - RxSwift 差异库崩溃 `NSInternalInconsistencyException`,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58587566/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com