1

J'utilise un NSFetchedResultsController de manière standard pour mettre à jour un UITableView chaque fois que quelque chose change dans les entités de données de base associées. Je fais la même chose que décrit in the Apple documentation.Délégué NSFetchedResultsController allouant trop de UITableViewCells

Le problème que j'ai est quand je fais une insertion en masse de nouvelles entités de données de base. Cela provoque le délégué NSFetchedResultsController à allouer (et insérer) une nouvelle cellule pour chaque entité, mais il le fait sans recycler les UITableViewCells (par exemple, dequeueReusableCellWithIdentifier: renvoie toujours null). Cela signifie l'allocation de potentiellement 100s de UITableViewCells, ce qui peut entraîner des problèmes de mémoire. Est-ce que quelqu'un sait d'un correctif ou d'une solution de contournement? Merci.

Edit 1:

Dans ma sous-classe UITableViewController j'ai les méthodes standard NSFetchedResultsControllerDelegate. Je crois que c'est identique à l'exemple d'Apple.

-(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:UITableViewRowAnimationTop]; 
      break; 

     case NSFetchedResultsChangeDelete: 
      [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationBottom]; 
      break; 

     case NSFetchedResultsChangeUpdate: 
      [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; 
      break; 

     case NSFetchedResultsChangeMove: 
      [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationBottom]; 
      // Reloading the section inserts a new row and ensures that titles are updated appropriately. 
      [tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationTop]; 
      break; 
    } 
} 

-(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:UITableViewRowAnimationTop]; 
      break; 

     case NSFetchedResultsChangeDelete: 
      [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationBottom]; 
      break; 
    } 
} 

-(void)controllerDidChangeContent:(NSFetchedResultsController *)controller { 
    // The fetch controller has sent all current change notifications, so tell the table view to process all updates. 
    [self.tableView endUpdates]; 
} 

également dans mon sous-classe UITableViewController je donne les résultats suivants pour aller chercher une cellule pour une indexPath donnée:

-(void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { 
    Waypoint *waypoint = [self.fetchedResultsController objectAtIndexPath:indexPath]; 
    cell.textLabel.text = [waypoint comment]; 
} 

-(UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {  
    static NSString *CellIdentifier = @"WaypointCell"; // matches identifier in XIB 

    UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier]; 
    if (cell == nil) { 
     NSLog(@"new cell"); 
     cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];  
     cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 
    } else { 
     NSLog(@"recycled cell");  
    } 

    [self configureCell:cell atIndexPath:indexPath]; 

    return cell; 
} 

Dans la fonction tableView:cellForRowAtIndexPath: j'ai ajouté des déclarations NSLog à afficher ce qui se passe.

Voici ce qui se passe. Le UITableViewController mentionné ci-dessus est poussé sur la pile de navigation. Une requête asynchrone s'éteint et récupère un tas de données, et crée ou modifie les données relatives à ce fetchController. Une fois que [self.tableView endUpdates] est appelé, le système commence à créer et à insérer les UITableViewCells dans UITableView. Dans la console du débogueur, la sortie "new cell" est imprimée plusieurs fois (nombre possible dans les 100), ce qui est le cas pour chaque nouvelle entité créée. Ce n'est qu'après le chargement de la vue de table (si elle ne s'est pas bloquée à cause de problèmes de mémoire) et que je commence à faire défiler la sortie "cellule recyclée" dans la console.

+0

Je viens de remarquer que vous avez un total de 4 questions et aucun d'entre eux ont une réponse acceptée. Si la réponse résout votre problème, vous devez accepter la réponse en cliquant sur le signe vert. :) – Florin

+0

Vous devrez montrer votre code. Ce n'est pas un comportement normal. Le délégué ne crée pas lui-même de cellules. Vous insérez incorrectement les cellules quelque part. – TechZen

+0

Florin: Je vais revoir et mettre à jour le cas échéant. Merci pour le heads-up. TechZen: J'ai ajouté du code à la publication originale. J'espère que cela éclaircira le problème. A l'origine, j'ai utilisé une tableview cell personnalisée avec un fichier .xib, mais j'ai simplifié le code pour utiliser un UITableViewCell standard et le comportement est exactement le même. Corrigez que le délégué ne crée pas les cellules.C'est l'appel [tableView endUpdates] du délégué qui le fait. Merci. – chris

Répondre

1

J'ai trouvé une solution dont je suis content. Le problème n'est pas NSFetchedResultsController en soi, mais plutôt qu'il appelle [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationTop] potentiellement des centaines de fois à partir du délégué NSFetchedResultsController.

Ma solution est d'introduire un booléen massUpdate qui contrôle si le NSFetchedResultsController devrait insérer les nouvelles lignes de table comme ci-dessus, ou s'il devrait faire un [tableView reloadData] simple à la place. Je l'ai défini en fonction du nombre de lignes que je prévois d'insérer. Les détails de la mise en œuvre peuvent être trouvés au poste de forum suivant:

https://devforums.apple.com/message/181219#181219

+1

En travaillant sur un nouveau projet, j'ai trouvé le commentaire suivant dans le fichier modèle RootViewController.m d'Apple sous les méthodes NSFetchedResultsControllerDelegate: "Implémenter les méthodes ci-dessus pour mettre à jour la vue tabulaire en réponse à des changements individuels peut avoir des conséquences sur les performances. Si cela s'avère être un problème, vous pouvez simplement implémenter controllerDidChangeContent: qui informe le délégué que toutes les modifications de section et d'objet ont été traitées. " Il semble que ce soit un problème connu. – chris

+0

où avez-vous trouvé cet extrait de pomme? Merci! – jpswain

+0

Je l'ai trouvé dans un modèle de projet fourni avec XCode. Créez un nouveau projet, sélectionnez l'application maître-détail et assurez-vous que l'option Utiliser les données de base est cochée. Vous trouverez le commentaire dans le MasterViewController.m. – chris

1

Etes-vous sûr d'utiliser exactement le même identifiant pour créer et déqueueer la cellule? (même identifiant pour dequeueReusableCellWithIdentifier: et initWithStyle:reuseIdentifier:)

+0

Je suis tout à fait certain que c'est correct. Je mets une instruction NSLog à différents endroits pour surveiller le comportement, et tout fonctionne correctement sur la navigation de table normale (défilement vers le haut et vers le bas). Au chargement de la table, je vois une allocation initiale de 5 ou 6 cellules, et quand je fais défiler vers le haut et vers le bas, elles sont réutilisées. C'est seulement avec une mise à jour de la table de masse (le problème décrit à l'origine) que je vois que les 100 sont alloués. De plus, après l'allocation de masse, la plupart sont désalloués. Je suppose que ce sont les tablecells non visibles puisque le défilement revient ensuite au comportement attendu. – chris

Questions connexes