2012-07-25 5 views
1

J'ai une application iPad où je suis en utilisant le NSUndoManager avec Core Data. Les choses fonctionnent généralement bien, sauf qu'il y a un bug semi-reproductible quand je défais/refais plusieurs fois. Je travaille seulement sur le thread principal (au moins, j'ai désactivé MagicalRecords d'utiliser un NSManagedObject sur un thread secondaire.) Le problème se produit toujours si j'essaye d'annuler/refaire une insertion d'un NSManagedObject au contexteCoreData jette exception avec NSUndoManager

Ainsi J'ai quelque chose comme ceci:

if (!self.undoManager.isUndoing && !self.undoManager.isRedoing) 
{ 
    [self.undoManager undo]; 
} 
else 
{ 
    NSLog(@"gotcha!"); 
} 

Et après plusieurs fois, je reçois l'exception suivante Il arrive sur un fil secondaire, ce qui me fait penser à Core Data fait quelque chose en arrière-plan

CoreData: error: Serious application error. Exception was caught during Core Data 
change processing. This is usually a bug within an observer of 
NSManagedObjectContextObjectsDidChangeNotification. _registerUndoObject:: NSUndoManager 
0xcea2d60 is in invalid state, must begin a group before registering undo 
with userInfo (null) 2012-07-25 15:42:26.850 TT[3972:3c07] *** Terminating app due to 
uncaught exception 'NSInternalInconsistencyException', reason: '_registerUndoObject:: 
NSUndoManager 0xcea2d60 is in invalid state, must begin a group before registering undo 
..

Parfois, je suis aussi gettin g EXEC_BAD_ACCESS, d'autres fois juste l'exception ci-dessus.

Une idée de ce qui pourrait causer cela?

Edit: la situation clarifiée pour mundi (voir commentaires)

+0

Vous devez donner un contexte plus que juste l'appel à 'undo'. – Mundi

+0

Lorsque l'annulation se produit, je reconstruis l'interface utilisateur, ce qui est une opération assez coûteuse (reconstruction d'un grand nombre d'UIViews personnalisés à partir des NSManagedObjects d'origine). Cela sera finalement optimisé. Il serait difficile de fournir un code découpé ici (c'est un gros projet) - qu'est-ce qui vous serait utile? –

+0

Qu'est-ce que vous perdez? – Mundi

Répondre

0

Voici la solution qui a arrêté tous mes accidents: Apparemment Magical enregistrements utilise par défaut privateQueue concurrency, et si votre code est pas thread-safe, je suppose que les choses ne fonctionnent pas. Ce que je l'ai fait est changer de NSPrivateQueueConcurrencyType à NSMainQueueConcurrencyType pour la méthode suivante:

+ (NSManagedObjectContext *) MR_contextWithoutParent; 
{ 
    NSManagedObjectContext *context = [[self alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
return context; 
} 

Je trouve aussi un autre détail qui a travaillé ici: Core Data deleteObject: sets attributes to nil

ce que je devais faire était sauver le contexte de l'objet géré avant d'ajouter/supprimer un objet géré. Un peu bizarre et inefficace, mais qui évite beaucoup de problèmes.

0

façon standard pour annuler l'insertion d'un objet géré est

[self.managedObjectContext deleteObject:theManagedObject]; 
// If you have saved already, you would need to save again. 

Pas besoin d'utiliser le mécanisme d'annulation. Comme cela, vous codez devient

  • beaucoup plus lisible (vous dites explicitement ce que vous voulez faire) et
  • beaucoup moins sujette à l'erreur (pas d'événement imprévus avec des API plus complexes).
+0

Si j'utilise un gestionnaire d'annulation pour le reste de mes actions d'annulation, comment intercepteriez-vous une opération de suppression, puis gérerais cette custom? Il semblerait que je devrais garder une trace de ma propre pile d'annulation/rétablissement –

+0

D'accord, j'ai mal compris ce que vous faisiez. Que diriez-vous, dans le "gotcha!" En partie, vous envoyez juste l'annulation sur un autre thread? – Mundi

+0

Il semble ne plus jamais atteindre la partie gotcha.J'essaie de trouver un bon moyen de savoir quand les données de base sont traitées. J'ai essayé d'appeler processChanges avant de défaire, mais cela ne semble pas aider. J'ai même essayé de faire un PerformSelect: withDelay: 0 à passer à la prochaine boucle d'exécution dans le cas où le undoManager actuellement défaisait/refaisant: si (self.undoManager.isUndoing || self.undoManager.isRedoing) { NSLog (@ "question ici saut!"); [self performSelector: @selector (didRedoChange :) withObject: notification afterDelay: 0]; } –

Questions connexes