Migration légère d’un document NSPersistentDocument

J’essaie de faire une migration légère d’un magasin SQLite dans Core Data. Travailler sur Lion 10.7.3 avec Xcode 4.3.1.

Dans ma sous-classe NSPersistentDocument (AccountDocument), j’ai remplacé la méthode utilisée pour configurer le coordinateur du stockage permanent afin d’obtenir les options appropriées pour la migration:

- (BOOL)configurePersistentStoreCoordinatorForURL:(NSURL *)url ofType:(NSSsortingng *)fileType modelConfiguration:(NSSsortingng *)configuration storeOptions:(NSDictionary *)storeOptions error:(NSError **)error { NSMutableDictionary *newStoreOptions; if (storeOptions == nil) { newStoreOptions = [NSMutableDictionary dictionary]; } else { newStoreOptions = [storeOptions mutableCopy]; } [newStoreOptions setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption]; [newStoreOptions setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption]; BOOL result = [super configurePersistentStoreCoordinatorForURL:url ofType:fileType modelConfiguration:configuration storeOptions:newStoreOptions error:error]; return result; } 

(Merci à Malcolm Crawford pour cette astuce: http://homepage.mac.com/mmalc/CocoaExamples/controllers.html )

Lorsque je lance l’application, il échoue dans l’implémentation de -managedObjectModel par NSPersistentDocument:

 * thread #1: tid = 0x2703, 0x00007fff931d9350 libobjc.A.dylib`objc_msgSend_vtable13 + 16, stop reason = EXC_BAD_ACCESS (code=13, address=0x0) frame #0: 0x00007fff931d9350 libobjc.A.dylib`objc_msgSend_vtable13 + 16 frame #1: 0x00007fff8935e975 CoreData`-[NSKnownKeysDictionary1 _setValues:retain:] + 197 frame #2: 0x00007fff8935f288 CoreData`_newReadModelFromBytes + 648 frame #3: 0x00007fff8935b93e CoreData`+[NSManagedObjectModel(_NSManagedObjectModelPrivateMethods) _newModelFromOptimizedEncoding:error:] + 9310 frame #4: 0x00007fff89359451 CoreData`-[NSManagedObjectModel(_NSManagedObjectModelPrivateMethods) initWithContentsOfOptimizedURL:] + 305 frame #5: 0x00007fff89358d7b CoreData`-[NSManagedObjectModel initWithContentsOfURL:] + 443 frame #6: 0x00007fff893e9519 CoreData`+[NSManagedObjectModel mergedModelFromBundles:] + 377 frame #7: 0x00007fff8ded7037 AppKit`-[NSPersistentDocument managedObjectModel] + 301 frame #8: 0x00007fff8ded70b3 AppKit`-[NSPersistentDocument managedObjectContext] + 75 frame #9: 0x00007fff8ded6e3f AppKit`-[NSPersistentDocument _persistentStoreCoordinator] + 18 frame #10: 0x00007fff8ded6b5d AppKit`-[NSPersistentDocument configurePersistentStoreCoordinatorForURL:ofType:modelConfiguration:storeOptions:error:] + 51 frame #11: 0x0000000100003193 BeanCounter`-[AccountDocument configurePersistentStoreCoordinatorForURL:ofType:modelConfiguration:storeOptions:error:] + 419 at AccountDocument.m:298 

D’après ce que je peux dire de la documentation, l’implémentation par défaut ressemble à ceci:

 - (id)managedObjectModel { NSManagedObjectModel *result = [NSManagedObjectModel mergedModelFromBundles:nil]; return result; } 

Donc, pour déboguer un peu plus le problème, j’ai remplacé cette méthode par ceci:

 - (id)managedObjectModel { NSBundle *bundle = [NSBundle mainBundle]; NSURL *url = [bundle URLForResource:@"AccountDocument2" withExtension:@"momd"]; NSManagedObjectModel *result = [[[NSManagedObjectModel alloc] initWithContentsOfURL:url] autorelease]; return result; } 

(Merci à Jeff LaMarche pour l’idée: http://iphonedevelopment.blogspot.com/2009/09/core-data-migration-problems.html )

Le bundle et l’url indiquent les endroits auxquels je m’attends (et j’ai suivi les conseils de Marcus Zarra pour nettoyer le projet afin qu’il n’y ait pas de bundles .mom ou .momd parasites dans le package d’application: Utilisation de mergedModelFromBundles: et versioning (CoreData) ). Pourtant, l’application continue de se bloquer lors du chargement du modèle depuis l’URL.

J’ai vérifié que le AccountDocument2.xcdatamodeld est un package qui possède deux modèles pour le contrôle des versions: AccountDocument 2.xcdatamodel et (l’original) AccountDocument.xcdatamodel. Le menu contextuel “Versioned Core Data Model” dans les propriétés du fichier est défini sur “AccountDocument 2”.

La seule différence entre les deux modèles est qu’une entité possède un atsortingbut supplémentaire (et facultatif). Ma compréhension est que qualifie le modèle pour une migration légère.

Evidemment, je fais quelque chose de mal ici, mais je n’ai aucune idée de quoi. Toute aide sera très appréciée…

Mettre à jour:

La suggestion de Per Martin (et une vérification de la documentation de NSPersistentDocument) J’ai essayé d’utiliser ce code pour l’accesseur:

 - (id)managedObjectModel { static id sharedManagedObjectModel = nil; if (sharedManagedObjectModel == nil) { NSBundle *bundle = [NSBundle mainBundle]; NSURL *url = [bundle URLForResource:@"AccountDocument2" withExtension:@"momd"]; sharedManagedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:url]; } return sharedManagedObjectModel; } 

Toujours en train de s’écraser…

Mettre à jour

Après quelques suggestions sur Twitter, je suis passé à Xcode 4.3.2, mais les problèmes persistent.

MISE À JOUR DE RAGE

Je viens de créer le package de modèle versionné (AccountDocument2.xcdatamodeld) en utilisant Xcode 4.2 sur Snow Leopard. Après avoir créé et exécuté l’application, tout fonctionne comme prévu.

J’ai ensuite repris le paquet de fichiers AccountDocument2.xcdatamodeld sur Lion et Xcode 4.3.2. Lorsque je construis et exécute l’application, il continue à se bloquer lors du chargement de la ressource .momd. Oui les enfants, cela signifie que Xcode 4.3.x et le Data Model Comstackr (MOMC) sont à blâmer. Je ne vois pas d’autre solution que de faire toutes mes compilations sur Snow Leopard.

Je ne suis pas du genre à bash Xcode 4, mais lorsque nous nous trouvons dans une situation où la chaîne d’outils ne peut pas produire un fichier opaque (.mom et .momd) à partir d’une spécification opaque (.xcdatamodel et .xcdatamodeld) être optimiste quant à l’état des outils Mac et iOS. Il est ridicule qu’un composant de base de ces plates-formes ne réponde pas au niveau où je ne peux pas créer et exécuter mon application sur la dernière version du SDK et des outils de développement.

C’est venu à cette mise à jour

Plus de preuve qu’il s’agit d’un bogue grave du compilateur de modèle de données (MOMC) dans Xcode 4.3.2: si je copie le fichier .momd du dossier Resource créé par Xcode 4.2 dans mon projet et que je l’ajoute à la version en tant que fichier de copie phase de construction, l’application fonctionne bien.

J’ai aussi fait des tests où j’ai supprimé les règles de validation et les valeurs par défaut pour les atsortingbuts des différentes entités (basé sur la suggestion de Marcus ci-dessous). Aucun changement, le compilateur crée toujours un .momd invalide. J’ai également essayé de créer un modèle versionné où NOTHING a été modifié: le fichier .momd compilé a continué à tomber en panne. Donc, tout ce que vous avez dans vos modèles actuels (et les données qu’ils représentent) est la source du problème.

À noter également: ce bogue n’est pas isolé dans NSPersistentDocument (comme je le pensais à l’origine lorsque j’ai posé cette question). Je peux provoquer le blocage d’une application en utilisant simplement [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]

Pour l’instant, je vais éditer / modifier mes modèles en utilisant Xcode 4.2 sur Snow Leopard et transférer les ressources compilées vers Xcode 4.3.2 sur Lion. Si vous utilisez Core Data de quelque manière que ce soit, je vous suggère de faire de même jusqu’à ce que ce bogue soit résolu. Croyez-moi, vous passerez des jours à essayer de comprendre ce qui se passe si vous ne le faites pas.

Maintenant, soumettez un radar…

Mise à jour radar

Je viens de soumettre ce radar:

http://www.openradar.me/11184500

La mise à jour de Oh Crap It Must Lion

Je viens de télécharger et d’installer les outils Xcode 4.2 for Lion sur http://developer.apple.com/downloads . L’exemple d’application utilisé dans le radar se bloque toujours.

(Remarque: vous ne pouvez pas installer Xcode 4.2.1 car le certificate utilisé pour signer DeveloperTools.pkg a expiré. Seul Xcode 4.2 fonctionnera.)

Si vous êtes sous NDA, vous constaterez également que les outils bêta ne sont pas non plus utiles.

J’espère que vous avez une copie de Snow Leopard avec Xcode 4.2 autour: http://furbo.org/2012/03/28/vmware-for-developers/

Les demandes d’extraction WTF concernent la mise à jour d’entités et d’atsortingbuts versionnés

Via Evadne Wu sur Twitter:

https://twitter.com/#!/evadne/status/187625192342818818

Et comment elle l’a fait:

https://twitter.com/#!/evadne/status/187629091518816258

(Les fichiers .mom sont des listes binarys.)

La racine du problème est une demande de récupération unique. La façon dont cela se traduit par une migration des données d’un modèle à l’autre revient à un ingénieur chez Apple.

Les ressources .momd compilées peuvent être chargées après la modification d’une requête de récupération “existingPartner” par:

 name == $name 

à:

 name == $NAME 

Il est paradoxal qu’une partie du modèle object n’affectant pas la persistance des données interrompe la gestion des versions et la migration légère. Il ressort clairement de la documentation que cela ne devrait pas être le cas:

Le sharepoint vue de Core Data sur le contrôle de version est qu’il ne s’intéresse qu’aux caractéristiques du modèle qui affectent la persistance.

Utilisez la puissance de CHOCKLOCK pour corriger vos requêtes d’ extraction ou les supprimer complètement et utilisez les NSPredicates créés dans le code .

Je pense que vous devez stocker le modèle d’object géré dans une variable d’instance. Vous retournez un object auto-libéré, qui est probablement à l’origine du mauvais access.

D’après votre théorie selon laquelle il s’agit d’un problème avec le MOMC, avez-vous des règles de validation dans la mère?

J’ai vu des rapports où les règles de validation ne survivent pas au MOMC 4.x.

Cela pourrait être quelque peu lié à un problème que j’avais avec l’utilisation de Fetch Requests lorsque iOS5 est sorti en bêta. Cela provoquait un avertissement de build et mettait en panne l’application au lancement. Je n’utilisais pas vraiment la requête de récupération, alors je l’ai supprimée et tout s’est bien passé: Avertissement relatif aux données de base: “Les informations de version de hachage ne sont pas disponibles pour tous les modèles”