【问题标题】:How to fix "NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0."如何修复“NSInternalInconsistencyException”,原因:“无效更新:第 0 节中的行数无效。”
【发布时间】:2019-10-22 05:21:50
【问题描述】:

我正在使用 swift 创建一个新的 iOS 应用程序,并且需要帮助来让我的 tableView 删除数据。我正在使用 Core Data 以一对多的关系保存数据,并且我拥有它,因此选定的实体会被删除,但它在更新 tableView 时会崩溃。

我以前创建过应用程序,但这是我第一次使用一对多数据存储方法。我也用谷歌搜索了解决方案,但没有一个有效。这是我的 tableView 编辑样式的代码。

if editingStyle == .delete {
    context.delete(items[indexPath.row])

    do {
        try context.save()

        self.tableView.beginUpdates()
        items.remove(at: indexPath.row)
        self.tableView.deleteRows(at: [indexPath], with: .fade)
        self.tableView.endUpdates()
    } catch let error as NSError {
        print("Could not save. \(error.localizedDescription)")
    }
}

我希望该行与 Core Data 中的实体一起删除,但它实际上只是删除实体,然后在尝试更新 tableView 时崩溃。具体来说,我认为它在调用“deleteRows”时会崩溃,因为它会给出错误:

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

更多代码:

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    let count = fetchedRC.fetchedObjects?.count ?? 0
    return count
}


override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "itemsTable", for: indexPath) as! ItemsTableViewCell

    items.append(fetchedRC.object(at: indexPath))

    if items[indexPath.row].isComplete == true {
        let attributeString = NSMutableAttributedString(string: items[indexPath.row].name!)

        attributeString.addAttribute(NSAttributedString.Key.strikethroughStyle, value: NSUnderlineStyle.single.rawValue, range: NSMakeRange(0, attributeString.length))
        cell.nameL.attributedText = attributeString

    } else {
        cell.nameL.text = items[indexPath.row].name
    }

    return cell
}

【问题讨论】:

  • 尝试交换item.remove...beginUpdates 行。
  • 这也不起作用,我用完整的错误更新它并删除带有错误的评论以减少混乱。
  • 您能出示您的numberOfRowscellForRow 代码吗?
  • @Paulw11 我用现在包含的 numberOfRows 和 CellForRow 更新了问题

标签: ios swift uitableview core-data crash


【解决方案1】:

您正在使用NSFetchedResultsController,因此维护一个单独的数组(您的items)数组不是一个好主意;只需在需要时使用object(at:) 方法获取项目。

您还有一个问题,因为您将项目附加到 cellForRowAt: 中的 items 数组 - 对于给定的 indexPathcellForRowAt: 将被多次调用,并且无法保证其中的顺序cellForRowAt: 被调用(例如,当您滚动表格视图时)。

如果您使用的是NSFetchedResultsController,您确实应该实现它的委托并使用委托方法来更新您的表格。

摆脱items 数组并使用类似的东西:

extension ItemsTableViewController: NSFetchedResultsControllerDelegate {
    func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        self.tableView.beginUpdates()
    }

    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {

        let newIndexPath: IndexPath? = newIndexPath != nil ? IndexPath(row: newIndexPath!.row, section: 0) : nil
        let currentIndexPath: IndexPath? = indexPath != nil ? IndexPath(row: indexPath!.row, section: 0) : nil

        switch type {
        case .insert:
            self.tableView.insertRows(at: [newIndexPath!], with: .automatic)

        case .delete:
            self.tableView.deleteRows(at: [currentIndexPath!], with: .fade)

        case .move:
            self.tableView.moveRow(at: currentIndexPath!, to: newIndexPath!)

        case .update:
            self.tableView.reloadRows(at: [currentIndexPath!], with: .automatic)
        }
    }

    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        self.tableView.endUpdates()
    }
}

您的cellForRowAt 应该是:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "itemsTable", for: indexPath) as! ItemsTableViewCell

    let item = fetchedRC.object(at:indexPath)

    if item.isComplete == true {
        let attributeString = NSMutableAttributedString(string: item.name ?? "")

        attributeString.addAttribute(NSAttributedString.Key.strikethroughStyle, value: NSUnderlineStyle.single.rawValue, range: NSMakeRange(0, attributeString.length))
        cell.nameL.attributedText = attributeString

    } else {
        cell.nameL.text = item.name
    }

    return cell
}

你的删除代码应该是:

if editingStyle == .delete {
    context.delete(fetchedRC.object(at:indexPath))

    do {
        try context.save()
    } catch let error as NSError {
        print("Could not save. \(error.localizedDescription)")
    }
}

请记住在创建获取的结果控制器时将self 分配给fetchedRC.delegate

【讨论】:

  • 好的,谢谢。我实现了这一点并且删除仍然有效,但是当我删除一行时似乎没有调用扩展。为什么会这样?同样作为参考,我的视图控制器名称是“ItemsTableViewController”,如果这对扩展有帮助的话。我还更改了扩展名以扩展此视图。
  • 您是否将self 分配给fetchedRC.delegate
  • 另外,我错过了删除代码中的一项必要更改 - 我已修复它
  • 我没有。我如何分配 selfas fetchedRC.delegate 或者那会在哪里?我注意到了这个要求并改变了它,不过感谢你指出这一点。
  • 假设您在 viewDidLoad 或类似名称中创建获取的结果控制器,您只需说 fetchedRC.delegate = self
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-02
  • 2014-10-28
  • 1970-01-01
  • 1970-01-01
  • 2016-07-05
  • 2013-07-28
相关资源
最近更新 更多