间歇性崩溃:尝试从更新前仅包含 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 是一个已经实现此功能的解决方案,您可能会通过使用它获得价值。
我们已经为我们的应用程序实现了滑动删除功能,但不知何故,我们在 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 是一个已经实现此功能的解决方案,您可能会通过使用它获得价值。