间歇性崩溃:尝试从更新前仅包含 0 行的第 0 节中删除第 0 行

Intermittent Crash: Attempt to delete row 0 from section 0 which only contains 0 rows before the update

我们已经为我们的应用程序实现了滑动删除功能,但不知何故,我们在 Crashlytics 上看到了这种间歇性的 prod 崩溃。

我一直在关注关于这次崩溃的几篇 Whosebug 帖子,但我无法找到这次崩溃的确切原因。

我曾多次尝试制造此崩溃,但每次都运行良好。

任何想法或想法,如果我在这里做错了什么?下面是崩溃报告和当前有效的代码片段。

@available(iOS 11.0, *)
private func actionForType(alertID: String, swipeAction: AlertSwipeActionType, indexPath: IndexPath) -> UIContextualAction {
    let contexualAction = UIContextualAction(style: .normal, title: nil) { [weak self] (action, view, completion) in

        guard let strongSelf = self else {
            return
        }

        switch swipeAction {
        ......
        case .affirm:
            completion(true)
            strongSelf.dispositionAlert(id: alertID, status: true, indexPath: indexPath)
        ......
        }
    }

    ......
    return contexualAction
}

fileprivate func dispositionAlert(id: String, status: Bool, indexPath: IndexPath) {
    let dispositionRequest = AlertDispositionUpdateBody(id: id, disposition: status, questionId: nil)
    self.updateAlertDispositionStatus(request: dispositionRequest) { [weak self] in

        guard let strongSelf = self else {
            return
        }
        strongSelf.removeCellWithAnimationAt(indexPath: indexPath)
        strongSelf.loadAlerts()
    }
}

fileprivate func removeCellWithAnimationAt(indexPath: IndexPath) {
    DispatchQueue.main.async {
        self.tableView.beginUpdates() // likely not required
        self.removeAlertAtIndexPath(indexPath)
        self.tableView.deleteRows(at: [indexPath], with: .fade)
        self.tableView.endUpdates() // likely not required either
    }
}

@objc func loadAlerts() {
    self.startLoadingAnimation()
    self.alertsFooterList.removeAll()

    AlertsManager.sharedInstance.loadMemberAlerts()
}

fileprivate func removeAlertAtIndexPath(_ indexPath: IndexPath) {
    let alertStatus = self.alertTabType.statusToLoad[indexPath.section]
    if let alerts = self.alertsList[alertStatus], 
       alerts.count > indexPath.row {
       self.alertsList[alertStatus]?.remove(at: indexPath.row)
    }
}

如果你使用tableview delete row called然后首先调用section number。 下面的代码。

代码: swift

fileprivate func removeCellWithAnimationAt(indexPath: IndexPath) {
    DispatchQueue.main.async {
        self.removeAlertAtIndexPath(indexPath)
        self.tableView.numberOfRows(inSection: indexPath.section) // called first
        self.tableView.deleteRows(at: [indexPath], with: .fade)
    }
}

我希望这段代码有效。

崩溃告诉您发生了什么:当您尝试删除该行时,它已被删除。现在这看起来很奇怪,因为当您显示 alertView 时该行就在那里。因此,在显示 alertView 和删除该行之间,该行已被其他来源删除。阅读您的代码显然是可能的。在显示警报视图和删除行之间有两次延迟。第一个是用户确认删除之前可能需要很长时间,第二个是 DispatchQueue.main.async,它通常很快,但仍然会导致这些类型的错误。

不要记录要删除的索引路径(因为索引路径可能在用户确认时发生变化),而是记录您要删除的项目的ID。当用户最终确认警报时,然后查找它在表视图中的位置,如果找到它则将其删除。

更深层次的问题是你有两个事实来源——表格视图和你的数据源,但它们不同步。最好先更新你的数据源,然后有自动同步表视图的代码。这样他们总是保持同步。 https://github.com/Instagram/IGListKit 是一个已经实现此功能的解决方案,您可能会通过使用它获得价值。