web-dev-qa-db-fra.com

NSFetchedResultsController se bloque sur performFetch: lors de l'utilisation d'un cache

J'utilise NSFetchedResultsController pour afficher un tas d'objets, qui sont sectionnés en utilisant des dates. Lors d'une nouvelle installation, tout fonctionne parfaitement et les objets sont affichés dans la vue de table. Cependant, il semble que lorsque l'application est relancée, j'obtiens un plantage. Je spécifie un cache lors de l'initialisation de NSFetchedResultsController, et quand je ne le fais pas, cela fonctionne parfaitement.

Voici comment je crée mon NSFetchedResultsController:

- (NSFetchedResultsController *)results {
    // If we are not nil, stop here
    if (results != nil)
        return results;

    // Create the fetch request, entity and sort descriptors
    NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext:self.managedObjectContext];
    NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:@"utc_start" ascending:YES];
    NSArray *descriptors = [[NSArray alloc] initWithObjects:descriptor, nil];

    // Set properties on the fetch
    [fetch setEntity:entity];
    [fetch setSortDescriptors:descriptors];

    // Create a fresh fetched results controller
    NSFetchedResultsController *fetched = [[NSFetchedResultsController alloc] initWithFetchRequest:fetch managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"day" cacheName:@"Events"];
    fetched.delegate = self;
    self.results = fetched;

    // Release objects and return our controller
    [fetched release];
    [fetch release];
    [descriptor release];
    [descriptors release];
    return results;
}

Voici les messages que je reçois lorsque l'application se bloque:

FATAL ERROR: The persistent cache of section information does not match the current configuration.  You have illegally mutated the NSFetchedResultsController's fetch request, its predicate, or its sort descriptor without either disabling caching or using +deleteCacheWithName:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'FATAL ERROR: The persistent cache of section information does not match the current configuration.  You have illegally mutated the NSFetchedResultsController's fetch request, its predicate, or its sort descriptor without either disabling caching or using +deleteCacheWithName:'

Je n'ai vraiment aucune idée de pourquoi cela dit cela, car je ne crois pas que je fais quoi que ce soit de spécial qui pourrait causer cela. Le seul problème potentiel est l'en-tête de section (jour), que je construis comme ceci lors de la création d'un nouvel objet:

// Set the new format
[formatter setDateFormat:@"dd MMMM"];

// Set the day of the event
[event setValue:[formatter stringFromDate:[event valueForKey:@"utc_start"]] forKey:@"day"];

Comme je l'ai mentionné, tout cela fonctionne bien s'il n'y a pas de cache impliqué. Toute aide appréciée!

47
Oliver

J'ai eu un problème similaire avec l'une de mes applications, lorsque le Apple a publié le nouvel iOS 4.0. Recherche:

fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[self managedObjectContext] sectionNameKeyPath:nil cacheName:nil];

Et définissez la valeur du paramètre cacheName sur nil. Cela a fonctionné pour moi, j'espère que ce sera le cas pour vous. Faites le moi savoir.

65
IssamTP

J'ai commencé à obtenir la même erreur lors de la mise à niveau de MacBook Pro vers Snow Leopard 10.6.4 et le dernier SDK.

Il s'avère que beaucoup d'entre nous utilisaient du code qui n'était pas conforme aux règles, mais nous ne le savions pas parce que CoreData ne se comportait pas vraiment conformément à ses propres règles.

Plus précisément, lorsque vous récupérez des éléments, ils sont mis en cache et, dans la version 4.0, ce cache n'est pas automatiquement purgé dans les cas où il a été purgé dans le SDK précédent.

Pour moi, la solution était simple. Je viens d'employer la méthode de classe qui purge les caches. Vous pouvez spécifier une entité individuelle, mais je spécifie nil donc il les fait tout simplement dans ce morceau particulier de code de démarrage:

[NSFetchedResultsController deleteCacheWithName:nil];

Du coup, la petite application sur laquelle j'ai travaillé uniquement pour me familiariser avec CoreData fonctionne à nouveau.

30
Mike V

Directement dans la documentation de NSFetchedResultsController :

Modification de la demande de récupération

Vous ne pouvez pas simplement modifier la demande d'extraction pour modifier les résultats. Si vous souhaitez modifier la demande de récupération, vous devez:

  1. Si vous utilisez un cache, supprimez-le (en utilisant deleteCacheWithName:). En règle générale, vous ne devez pas utiliser de cache si vous modifiez la demande de récupération.

  2. Modifiez la demande de récupération.

  3. Appeler performFetch:.

15
Mark Adams

J'ai rencontré un problème similaire. Lorsque j'ai inspecté la console du débogueur, elle a montré quels étaient les objets mis en cache et les objets récupérés afin que je puisse comprendre pourquoi ils sont incohérents. Dans mon cas, cela était dû à un prédicat différent.

Étant donné que les valeurs de mon prédicat ne sont pas dynamiques, je pourrais spécifier un nom de cache différent pour chaque prédicat. Cela créera un cache pour chaque "type" que je spécifie.

Je suppose que vous devrez évaluer votre besoin d'avoir le cache. Spécifier nil signifie qu'une extraction est effectuée dans chaque appel.

J'ai compris que l'erreur ne se produit que lorsque la demande de récupération comporte des modifications. Si vous créez un nouveau NSFetchRequest OR modification du prédicat OR descripteur de tri, vous devez supprimer le cache ou utiliser un autre cache. Sinon, assurez-vous que vous avoir la même NSFetchRequest ou assurez-vous que votre NSFetchedResultsController est conservé et cela devrait résoudre votre problème.

6
Terrence Tan

Si vous utilisez le simulateur, essayez de le réinitialiser - je suppose que vous avez changé votre carte d'entité et qu'il devient confus par un cache restant. Sinon, vous pouvez essayer de faire ce que l'erreur dit:

- (void)applicationWillTerminate:(UIApplication *)application {
    [NSFetchedResultsController deleteCacheNamed:@"Events"];
    //etc
}
3
shosti

L'exception se produit toujours avec Xcode 7 (beta 4):

Vous avez illégalement muté la demande d'extraction de NSFetchedResultsController, son prédicat ou son descripteur de tri sans désactiver la mise en cache ni utiliser + deleteCacheWithName:

REMARQUE: Ceci est avec une application iOS maître non détaillée de "modèle" Xcode avec Xcode CoreData standard "plaque de chaudière" code créé avec Xcode 7 et utilisant la dernière cible de déploiement (iOS 9).

Je l'ai remarqué en premier lorsque j'ai redémarré mon application dans le simulateur. J'avais démarré et arrêté l'application plusieurs fois via Xcode et puis c'est arrivé; et ça n'arrêtait pas. J'ai décidé de faire quelques expériences, et les résultats:

  • Chaque fois que j'arrêtais l'application dans le simulateur, je recevais l'exception lors d'un lancement ultérieur.
  • Chaque fois que j'arrêtais l'application à l'aide du bouton Accueil du simulateur, je pouvais la relancer avec succès.

Le problème peut toujours être résolu comme suit, en utilisant l'une ou l'autre des méthodes suivantes:

  • Dans la méthode application didFinishLaunchingWithOptions D'AppDelegate, ajoutez le code suivant Swift NSFetchedResultsController.deleteCacheWithName(nil) ou Objective-C [NSFetchedResultsController deleteCacheWithName:nil];. Cela efface le cache corrompu.
  • Dans le simulateur, dans le menu Simulateur, sélectionnez Réinitialiser le contenu et les paramètres. Cela résout le problème, mais vous perdez vos données de test.

Je crois également que c'est un artefact de l'exécution via Xcode et de l'arrêt de l'application avant qu'elle ne puisse être nettoyée. Je n'ai pas vu cela dans l'appareil réel.

3
rholmes

Pour ceux qui rencontrent le même problème de nos jours, le problème est que, d'une manière ou d'une autre, Core Data ne nettoie pas le cache, donc fonctionne bien la première fois, mais pas après. Ensuite, mettez simplement cette ligne juste après l'init NSFetchRequest

[NSFetchedResultsController deleteCacheWithName:@"Name"];
1
Vini Oliveira

Quittez-vous le simulateur à l'aide du bouton d'accueil ou en fermant l'application dans Xcode? Peut-être que l'application n'a pas le temps de terminer l'écriture dans le cache. Essayez d'utiliser le bouton d'accueil pour quitter l'application.

1
nevan king

J'avais le même problème.
Pour corriger, j'ai mis le [NSFetchedResultsController deleteCacheWithName:@"cacheName"]; avant le lancement de resultsController. Fonctionne pour moi car il n'y va que la première fois.

1
pablotj

J'ai trouvé via Ray Wenderlich forums que

Notez qu'il ne se bloque que lorsque la demande de récupération n'est pas encore créée lorsque vous ajoutez quelque chose de nouveau à la banque de données , c'est-à-dire lorsque la vue Emplacements n'est pas encore chargée . Si la vue est déjà chargée, cela fonctionne bien. Bizarre, hein?

Donc, ce qui s'est passé dans mon cas était le suivant:

  1. Le processus de lancement normal implique la construction d'un NSFetchedResultsController.
  2. Parce qu'il y a des milliers d'objets récupérés, une nouvelle récupération prend un temps considérable. Pour atténuer cela, l'extraction a) utilise un cache et b) se produit en arrière-plan, permettant à d'autres activités de continuer
  3. Normalement, bien que l'interface utilisateur soit réactive et que l'utilisateur puisse faire des choses, la récupération est terminée bien avant que l'utilisateur puisse exprimer un désir de créer un nouvel objet.
  4. Cependant, parfois, l'application sera lancée en arrière-plan - par exemple, à partir d'un événement WatchKit, ou d'une extraction en arrière-plan, etc.) - et une partie du lancement entraînera la création immédiate d'un nouvel objet dans le magasin de données.
  5. Si le nouvel objet était créé avant la fin de l'extraction, l'application se bloquerait.

La solution consiste à s'assurer que La récupération est terminée avant de créer un objet (ce qui affecterait la récupération).

Alternativement, vous pouvez supprimer le cache, mais c'est moins performant pratiquement.

Notez que l'avertissement du débogueur

Vous avez illégalement muté la demande d'extraction de NSFetchedResultsController, son prédicat ou son descripteur de tri sans désactiver la mise en cache ni utiliser + deleteCacheWithName:

ne saisit tout simplement pas cette situation, en ce que ce que vous avez changé n'était pas la demande, le prédicat ou le descripteur de tri, mais pourrait être décrit avec plus de précision comme ayant muté le jeu de résultats pendant que la récupération était en cours.

Il m'a fallu une éternité pour retrouver ce petit morceau de trivia. J'espère que vous en bénéficierez.

1
SG1

Combien de classes implémentent la même méthode de résultats - (NSFetchedResultsController *), utilisez-vous un cache différent pour chacune? J'avais le même problème et je pense que je le corrige en utilisant un nom de cache différent pour certains clases car j'ai différents NSPredicates.

1
jigzat