NSInternalInconsistencyException:使用 tableview CoreData 的无效更新

Posted

技术标签:

【中文标题】NSInternalInconsistencyException:使用 tableview CoreData 的无效更新【英文标题】:NSInternalInconsistencyException: Invalid Update using tableview CoreData 【发布时间】:2015-08-28 19:18:21 【问题描述】:

我正在使用tableView 来显示人员列表。我正在尝试添加警报以确认用户确实想要删除此人并防止错误。但是,当我尝试删除使用 CoreData 存储的人员时,重新加载视图似乎存在问题。我得到这个例外: 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 (2) must be equal to the number of rows contained in that section before the update (2), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'

编辑删除功能:

override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) 
    if editingStyle == .Delete 

        // Delete the row from the data source
        var deleteRow = indexPath.row

        indexPathforDelete = indexPath

        let entityDescription = NSEntityDescription.entityForName("People", inManagedObjectContext: managedObjectContext!)
        let request = NSFetchRequest()
        request.entity = entityDescription

        var error: NSError?

        var objects = managedObjectContext?.executeFetchRequest(request, error: &error)

        if let results = objects 

            let personToDelete = results[deleteRow] as! NSManagedObject
            let firstName = personToDelete.valueForKey("firstName") as! String
            let lastName = personToDelete.valueForKey("lastName") as! String

            var message = "Are you sure you would like to delete \(firstName) \(lastName)?\nThis will permanentaly remove all records of "

            if(personToDelete.valueForKey("gender") as! String == "Male")

                message = "\(message)him."

            

            else

                println(personToDelete.valueForKey("gender") as! String)

                message = "\(message)her."

            

            var deleteAlert : UIAlertView = UIAlertView(title: "Delete \(firstName) \(lastName)", message: message, delegate: self, cancelButtonTitle: "Cancel")

            deleteAlert.addButtonWithTitle("Delete")

            deleteAlert.show()

        

        save()

     else if editingStyle == .Insert 
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view

        

AlertView 响应函数:

func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int)

    if(buttonIndex == 1)
        managedObjectContext?.deleteObject(personToDelete)
        tableView.deleteRowsAtIndexPaths([indexPathforDelete], withRowAnimation: .Fade)
        save()
    

    setEditing(false, animated: true)
    self.navigationItem.leftBarButtonItem = nil


tableView行数函数:

var personToDelete = NSManagedObject()
var indexPathforDelete = NSIndexPath()

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
    // #warning Incomplete method implementation.
    // Return the number of rows in the section.

    let entityDescription = NSEntityDescription.entityForName("People", inManagedObjectContext: managedObjectContext!)
    let request = NSFetchRequest()
    request.entity = entityDescription

    var error: NSError?

    var objects = managedObjectContext?.executeFetchRequest(request, error: &error)

    let results = objects

    println("Results Count: \(results!.count)")

    return results!.count


【问题讨论】:

【参考方案1】:

认为问题是您有两个名称为propertyToDelete 的变量:您声明并使用空白NSManagedObject 初始化的属性:

        var personToDelete = NSManagedObject()

以及您在 commitEditingStyle 函数中声明的局部变量:

        let personToDelete = results[deleteRow] as! NSManagedObject

您将结果数组中的对象分配给这个局部变量。但是这个局部变量在函数完成时被销毁,并且 AlertView 操作正在删除 property 指向的对象。 (我犹豫的原因是我希望您的上下文在尝试删除从未向其注册的对象时抛出错误)。请注意,相比之下,您只有一个名为 indexPathforDelete 的变量。当 AlertView 操作运行时,它会保存正确的值,因此 tableView 会删除正确的行。这就是您收到错误的原因:它删除了一行,但随后发现(因为没有删除任何对象)它仍然具有与以前相同的行数。

直接的解决方案是在函数中使用属性,而不是局部变量:只需删除 let

        personToDelete = results[deleteRow] as! NSManagedObject

但我也建议重新考虑您的方法:您正在重复相同的提取。如果所有数据源方法都这样做,那么在第一次构建表格视图时,每当滚动单元格进入视图时,每当点击单元格时,都会重复多次。这在性能方面会很昂贵。相反,您应该进行一次提取(可能在 viewDidLoad 中),将结果存储在数组属性中,并将其用于表视图数据源方法。或者,也许最好使用NSFetchedResultsController:它非常有效,并且有用于在添加或删除对象时更新表视图的样板代码。

【讨论】:

谢谢,我不敢相信我错过了,我会试试的。【参考方案2】:

tableView:commitEditingStyle:forRowAtIndexPath: 的文档说:“您不应在此方法的实现中调用 setEditing:animated:。如果由于某种原因必须,请在延迟后使用 performSelector:withObject:afterDelay 调用它: 方法。”

【讨论】:

以上是关于NSInternalInconsistencyException:使用 tableview CoreData 的无效更新的主要内容,如果未能解决你的问题,请参考以下文章

无法在包中加载 Nib - 错误的 nibname 错误