usar tutorial porque persistencia efecto datos data apple iphone ios core-data ios4 nsfetchedresultscontroller

iphone - porque - swift 4 core data tutorial



NSFetchedResultsController ignora fetchLimit? (7)

Tengo un NSFetchedResultsController para actualizar un UITableView con contenido de Core Data. Es algo bastante estándar, estoy seguro de que todos lo han visto muchas veces, sin embargo, me estoy encontrando con un pequeño problema. Primero, aquí está mi código:

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"Article" inManagedObjectContext:self.managedObjectContext]; [fetchRequest setEntity:entity]; [fetchRequest setFetchLimit:20]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(folder.hidden == NO)"]; [fetchRequest setPredicate:predicate]; NSSortDescriptor *sort1 = [NSSortDescriptor sortDescriptorWithKey:@"sortDate" ascending:NO]; [fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sort1, nil]]; NSFetchedResultsController *controller = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil]; [fetchRequest release]; controller.delegate = self; self.fetchedResultsController = controller; [controller release]; NSError *error = nil; [self.fetchedResultsController performFetch:&error]; if (error) { // TODO send error notification NSLog(@"%@", [error localizedDescription]); }

El problema es que inicialmente la tienda no tiene entidades, ya que descarga y sincroniza desde un servicio web. Lo que sucede es que NSFetchedResultsController llena la tabla con más de 150 filas de entidades de la tienda, que es la cantidad que devuelve el servicio web. Pero estoy estableciendo un límite de búsqueda de 20 que parece estar ignorando. Sin embargo, si cierro la aplicación y empiezo de nuevo con los datos que ya están en la tienda, funciona bien. Soy mi delegado, hago esto:

#pragma mark - #pragma mark NSFetchedResultsControllerDelegate methods - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { [self.tableView beginUpdates]; } - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { switch(type) { case NSFetchedResultsChangeInsert: [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; break; } } - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { UITableView *tableView = self.tableView; switch(type) { case NSFetchedResultsChangeInsert: [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeUpdate: [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; break; case NSFetchedResultsChangeMove: [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; } } - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { [self.tableView endUpdates]; }

¿Qué es más o menos copiar y pegar de los documentos de desarrollo de Apple, alguna idea de lo que está pasando?


Archivé un informe de error con Apple en 2014 en iOS 6/7 sobre este tema. Como muchos otros han notado, todavía es un error en iOS 9 y 10. Mi informe de error original todavía está abierto sin comentarios de Apple. Aquí hay una copia de OpenRadar de ese informe de errores.

Aquí hay una solución que he utilizado con éxito, pero se llamará varias veces. Usar con precaución.

@objc func controllerDidChangeContent(controller: NSFetchedResultsController) { tableView.endUpdates() // Only needed if you''re calling tableView.beginUpdates() in controllerWillChangeContent. if controller.fetchRequest.fetchLimit > 0 && controller.fetchRequest.fetchLimit < controller.fetchedObjects?.count { controller.performFetch() // Reload the table view section here } } }


Desde Apple Doc: https://developer.apple.com/reference/coredata/nsfetchrequest/1506622-fetchlimit

Si establece un límite de búsqueda, el marco hace un mejor esfuerzo, pero no garantiza, para mejorar la eficiencia. Para cada almacén de objetos, excepto el almacén SQL, una solicitud de búsqueda ejecutada con un límite de búsqueda en efecto simplemente realiza una búsqueda ilimitada y arroja las filas no solicitadas.


El problema que tienes es que estás llamando antes de cargar fetchedResultsController carga la información completa para que te muestre que todo lo que tienes que hacer es cargar toda la información y luego llamar a fetchedResultsController

Ejemplo

- (void)viewDidLoad { [super viewDidLoad]; // Loading Articles to CoreData [self loadArticle]; } - (void)ArticleDidLoadSuccessfully:(NSNotification *)notification { NSError *error; if (![[self fetchedResultsController] performFetch:&error]) { // Update to handle the error appropriately. NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); // Fail } [tableView reloadData]; }


Esta es una vieja pregunta, pero me encontré con ella (en iOS 5). Creo que te encuentras con el error descrito aquí: https://devforums.apple.com/message/279576#279576 .

Ese hilo proporciona soluciones basadas en si tiene una sectionNameKeyPath o no. Como I (como usted) no lo hizo, la respuesta es desacoplar la vista de tabla de fetchedResultsController. Por ejemplo, en lugar de usarlo para determinar el número de filas:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [[[self.fetchedResultsController sections] objectAtIndex:0] numberOfObjects];

solo devuelve lo que esperas:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return fetchLimit;

Y en el controller:didChangeObject , solo inserte el nuevo objeto si newIndexPath está dentro de su fetchLimit.


Este es mi truco:

Configuro el delegado NSFetchedResultsController después de llamar al método ''guardar'' en la instancia de NSManagedObjectContext.

  1. Establezca un observador en su UIViewController con un nombre: ej. ''Sincronización''
  2. después de guardar su contexto, publique una notificación con ese nombre: ''Sincronizar'' y active una función (en su viewcontroller) que configure al delegado

PD. recuerde eliminar ese observador si ya no lo necesita


Esto aún se bloqueará en algunas situaciones, como varias inserciones, o se moverá por encima del límite, ... Debe guardar todos los cambios en 4 conjuntos, calcular otras 4 matrices y eliminar / actualizar / insertar en tableView antes de -[UITableView endUpdates]

Algo así como (supongamos que solo hay una sección):

NSUInteger limit = controller.fetchRequest.fetchLimit; NSUInteger current = <current section objects count>; NSMutableArray *inserts = [NSMutableArray array]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"row < %d", limit]; if (insertedIndexPaths.count) { NSUInteger deletedCount = 0; for (NSIndexPath *indexPath in insertedIndexPaths) { if (indexPath.row >= limit) continue; current++; if (current > limit) { deletedCount++; current--; [deletedIndexPaths addObject:[NSIndexPath indexPathForRow:limit - deletedCount inSection:indexPath.section]]; } [inserts addObject:indexPath]; } } if (movedIndexPaths.count) { for (NSIndexPath *indexPath in movedIndexPaths) { if (indexPath.row >= limit) { [updatedIndexPaths addObject:[NSIndexPath indexPathForRow:limit - 1 inSection:indexPath.section]]; } else { [inserts addObject:indexPath]; } } } [updatedIndexPaths minusSet:deletedIndexPaths]; [deletedIndexPaths filterUsingPredicate:predicate]; [updatedIndexPaths filterUsingPredicate:predicate]; [_tableView insertRowsAtIndexPaths:inserts withRowAnimation:UITableViewRowAnimationFade]; [_tableView reloadRowsAtIndexPaths:[updatedIndexPaths allObjects] withRowAnimation:UITableViewRowAnimationNone]; [_tableView deleteRowsAtIndexPaths:[deletedIndexPaths allObjects] withRowAnimation:UITableViewRowAnimationFade]; [_tableView endUpdates]; deletedIndexPaths = nil; insertedIndexPaths = nil; updatedIndexPaths = nil;


Sé que esta es una vieja pregunta, pero tengo una solución para esto:

Como hay un error conocido en NSFetchedResultsController que no NSFetchedResultsController el fetchlimit de NSFetchRequest , debe controlar manualmente la limitación de registros en sus métodos UITableViewDataSource y NSFetchedResultsControllerDelegate .

tableView: numberOfRowsInSection:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section]; NSInteger numRows = [sectionInfo numberOfObjects]; if (numRows > self.fetchedResultsController.fetchRequest.fetchLimit) { numRows = self.fetchedResultsController.fetchRequest.fetchLimit; } return numRows; }

controlador: didChangeObject: atIndexPath: forChangeType: newIndexPath:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { switch(type) { case NSFetchedResultsChangeInsert: if ([self.tableView numberOfRowsInSection:0] == self.fetchedResultsController.fetchRequest.fetchLimit) { //Determining which row to delete depends on your sort descriptors [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:self.fetchedResultsController.fetchRequest.fetchLimit - 1 inSection:0]] withRowAnimation:UITableViewRowAnimationFade]; } [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; ... } }