NSInternalInconsistencyException:使用 tableview CoreData 进行无效更新
NSInternalInconsistencyException: Invalid Update using tableview CoreData
我正在使用 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
}
我 认为 问题是你有两个名称为 propertyToDelete
的变量:一个 属性 你声明并用空白初始化 NSManagedObject
:
var personToDelete = NSManagedObject()
以及您在 commitEditingStyle
函数中声明的局部变量:
let personToDelete = results[deleteRow] as! NSManagedObject
您将结果数组中的对象分配给这个局部变量。但是这个局部变量在函数完成时被销毁,并且 AlertView 操作正在删除 属性 指向的对象。 (我犹豫的原因是我希望您的上下文在尝试删除从未注册过的对象时抛出错误)。请注意,相比之下,您只有一个名为 indexPathforDelete
的变量。这在 AlertView 操作运行时保存正确的值,因此 tableView 删除了正确的行。这就是你得到错误的原因:它删除了一行,但随后发现(因为没有对象被删除)它仍然具有与以前相同的行数。
直接的解决方案是在函数中使用 属性 而不是局部变量:只需删除 let
:
personToDelete = results[deleteRow] as! NSManagedObject
但我还建议重新考虑您的方法:您正在重复相同的提取。如果所有数据源方法都做同样的事情,那么在首次构建 table 视图时,无论何时将单元格滚动到视图中,无论何时点击单元格,都会重复多次。这将是昂贵的表现。相反,您应该进行一次提取(也许在 viewDidLoad 中),将结果存储在数组 属性 中,并将其用于 table 视图数据源方法。或者,也许最好使用 NSFetchedResultsController
:它非常高效,并且有用于在添加或删除对象时更新 table 视图的样板代码。
tableView:commitEditingStyle:forRowAtIndexPath 的文档:说:"You should not call setEditing:animated: within an implementation of this method. If for some reason you must, invoke it after a delay by using the performSelector:withObject:afterDelay: method."
我正在使用 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
}
我 认为 问题是你有两个名称为 propertyToDelete
的变量:一个 属性 你声明并用空白初始化 NSManagedObject
:
var personToDelete = NSManagedObject()
以及您在 commitEditingStyle
函数中声明的局部变量:
let personToDelete = results[deleteRow] as! NSManagedObject
您将结果数组中的对象分配给这个局部变量。但是这个局部变量在函数完成时被销毁,并且 AlertView 操作正在删除 属性 指向的对象。 (我犹豫的原因是我希望您的上下文在尝试删除从未注册过的对象时抛出错误)。请注意,相比之下,您只有一个名为 indexPathforDelete
的变量。这在 AlertView 操作运行时保存正确的值,因此 tableView 删除了正确的行。这就是你得到错误的原因:它删除了一行,但随后发现(因为没有对象被删除)它仍然具有与以前相同的行数。
直接的解决方案是在函数中使用 属性 而不是局部变量:只需删除 let
:
personToDelete = results[deleteRow] as! NSManagedObject
但我还建议重新考虑您的方法:您正在重复相同的提取。如果所有数据源方法都做同样的事情,那么在首次构建 table 视图时,无论何时将单元格滚动到视图中,无论何时点击单元格,都会重复多次。这将是昂贵的表现。相反,您应该进行一次提取(也许在 viewDidLoad 中),将结果存储在数组 属性 中,并将其用于 table 视图数据源方法。或者,也许最好使用 NSFetchedResultsController
:它非常高效,并且有用于在添加或删除对象时更新 table 视图的样板代码。
tableView:commitEditingStyle:forRowAtIndexPath 的文档:说:"You should not call setEditing:animated: within an implementation of this method. If for some reason you must, invoke it after a delay by using the performSelector:withObject:afterDelay: method."