如何修复“ NSInternalInconsistencyException”,原因:“无效的更新:第0节中的行数无效。”

时间:2019-06-06 16:40:09

标签: ios swift uitableview core-data crash

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

我以前创建过应用程序,但这是我第一次使用一对多数据存储方法。我也有谷歌解决方案,但没有一个工作。这是我的tableView editStyle的代码。

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
}

1 个答案:

答案 0 :(得分:0)

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

在将项目追加到items中的cellForRowAt:数组中时,您也遇到问题-对于给定的cellForRowAt:indexPath会被多次调用,并且在那里无法保证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