Core Data, establecer la relación en una entidad provoca un error en la búsqueda de resultados devuelve la devolución del controller

(Se cambiaron los nombres de las entidades para que sea más fácil de leer).

Medio ambiente: Xcode 7 beta 4. iOS 9.0 es el SDK base y el objective de implementación es iOS 8.0. Depuración en un dispositivo real iPhone 5s con iOS 8.4.

Aquí hay un error de Core Data interesante que he pasado los últimos días depurando / intentando entender.

He configurado una entidad, RecipeEntity , que tiene algunos attributes y una relación de muchos a muchos con la entidad IngnetworkingientEntity para representar sus ingnetworkingientes. IngnetworkingientsEntity tiene una relación inversa correspondiente a RecipeEntity , ambas entidades tienen las siguientes properties establecidas en las relaciones:

introduzca la descripción de la imagen aquí

Hay un controller de vista que tiene una vista de tabla que muestra los ingnetworkingientes, y usa un NSFetchedResultsController para get datos para las celdas, con el NSFetchedResultsControllerDelegate establecido en el propio controller de vista.

Aquí estaba el código original usado para el delegado del controller de resultados extraídos:

 // MARK: - FetchedResultsController Delegate /* We're using a nifty solution to fix the issue of multiple Core Data changes occuring at the same time. See here for an explanation of why and how it works: http://www.fruitstandsoftware.com/blog/2013/02/19/uitableview-and-nsfetchedresultscontroller-updates-done-right/ */ private var deletedSectionIndexes = NSMutableIndexSet() private var insertedSectionIndexes = NSMutableIndexSet() private var deletedRowIndexPaths = [NSIndexPath]() private var insertedRowIndexPaths = [NSIndexPath]() private var updatedRowIndexPaths = [NSIndexPath]() func controller(controller: NSFetchedResultsController, didChangeObject anObject: NSManagedObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { switch type { case NSFetchedResultsChangeType.Insert: if let newIndexPath = newIndexPath { if insertedSectionIndexes.containsIndex(newIndexPath.section) { // If we've already been told that we're adding a section for this inserted row we skip it since it will handled by the section insertion. return } insertedRowIndexPaths.append(newIndexPath) } case NSFetchedResultsChangeType.Delete: if let indexPath = indexPath { if deletedSectionIndexes.containsIndex(indexPath.section) { // If we've already been told that we're deleting a section for this deleted row we skip it since it will handled by the section deletion. return } deletedRowIndexPaths.append(indexPath) } case NSFetchedResultsChangeType.Move: if let newIndexPath = newIndexPath { if !insertedSectionIndexes.containsIndex(newIndexPath.section) { insertedRowIndexPaths.append(newIndexPath) } } if let indexPath = indexPath { if !deletedSectionIndexes.containsIndex(indexPath.section) { deletedRowIndexPaths.append(indexPath) } } case NSFetchedResultsChangeType.Update: if let indexPath = indexPath { updatedRowIndexPaths.append(indexPath) } } } func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { switch type { case NSFetchedResultsChangeType.Insert: insertedSectionIndexes.addIndex(sectionIndex) case NSFetchedResultsChangeType.Delete: deletedSectionIndexes.addIndex(sectionIndex) default: break } } func controllerDidChangeContent(controller: NSFetchedResultsController) { tableView.beginUpdates() tableView.deleteSections(deletedSectionIndexes, withRowAnimation: UITableViewRowAnimation.Automatic) tableView.insertSections(insertedSectionIndexes, withRowAnimation: UITableViewRowAnimation.Automatic) tableView.deleteRowsAtIndexPaths(deletedRowIndexPaths, withRowAnimation: UITableViewRowAnimation.Left) tableView.insertRowsAtIndexPaths(insertedRowIndexPaths, withRowAnimation: UITableViewRowAnimation.Right) tableView.reloadRowsAtIndexPaths(updatedRowIndexPaths, withRowAnimation: UITableViewRowAnimation.Automatic) tableView.endUpdates() // Clear the collections so they are ready for their next use. insertedSectionIndexes = NSMutableIndexSet() deletedSectionIndexes = NSMutableIndexSet() deletedRowIndexPaths = [NSIndexPath]() insertedRowIndexPaths = [NSIndexPath]() updatedRowIndexPaths = [NSIndexPath]() } 

El problema fue cuando se configuraba IngnetworkingientEntity la que RecipeEntity tenía una relación, se producía la siguiente falla:

08-12-2014 14: 43: 34.777 MyApp [9707: 2866157] *** Error de aserción en – [MyApp.MyAppTableView _endCellAnimationsWithContext:], /SourceCache/UIKit/UIKit-3347.44.2/UITableView.m:1222
08/08/2015 14: 43: 34.782 MyApp [9707: 2866157] CoreData: error: error grave de la aplicación. Se capturó una exception del delegado de NSFetchedResultsController durante una llamada a -controllerDidChangeContent :. intentar eliminar y volver a cargar la misma ruta de índice ({longitud = 2, ruta = 0 – 0}) con userInfo (null)

Finalmente controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: esto al controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: se llama con el tipo de cambio NSFetchedResultsChangeType.Move , pero el mismo NSIndexPath para both indexPath y newIndexPath .

Aquí está más o less el código que agrega las entidades como una relación:

 // setOfIngnetworkingients is an NSSet of IngnetworkingientEntity's retreived from Core Data. recipeEntity.ingnetworkingients = setOfIngnetworkingients 

Cambiar esta instrucción de caso de conmutador (en el controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: delegate method) en el siguiente corrige el problema:

  case NSFetchedResultsChangeType.Move: // If the indexPath and the newIndexPath are the same, then they shouldn't really be deleted and inserted, the row should just be reloaded, // otherwise it causes a crash. if indexPath != newIndexPath { if let newIndexPath = newIndexPath { if !insertedSectionIndexes.containsIndex(newIndexPath.section) { insertedRowIndexPaths.append(newIndexPath) } } if let indexPath = indexPath { if !deletedSectionIndexes.containsIndex(indexPath.section) { deletedRowIndexPaths.append(indexPath) } } } else if let indexPath = indexPath { updatedRowIndexPaths.append(indexPath) } 

Pregunta principal:

Entonces, ¿por qué establecer los valores de una relación establecida en un object de entidad de Core Data causa el controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: se llamará con el tipo de cambio. ¿Se .Move pero con las mismas routes de índice antiguas y nuevas?

¿Esto es un error en Xcode 7 beta 4 / Swift 2.0 o he hecho algo incorrectamente?

Me interesaría mucho entender lo que está sucediendo aquí, he pasado unos días tratando de depurar esto ahora, ¡entonces agradezco su ayuda!

Avíseme si hay más información que pueda dar que sea útil, gracias.

Me pregunto si esto se debe a que los cambios .Move se informan cuando el atributo cambiado en el object es uno de los descriptores de orderación utilizados en la request de obtención.

Veo que indexPath y newIndexPath son iguales a menudo también. Según los documentos, se considera que esto también es una actualización implícita.

Desde NSFetchedResultsController.h

 The Move object is reported when the changed attribute on the object is one of the sort descriptors used in the fetch request. An update of the object is assumed in this case, but no separate update message is sent to the delegate.