2017-04-25 3 views
0

J'utilise Core Data en combinaison avec mogenerator pour gérer un graphe d'objets de données relativement volumineux et fortement lié.CoreData + mogenerator - Comment empêcher `setValue (forKey:)` dans le datamodel intermédiaire de faire référence à la classe 'human' pour l'entité dans le dernier modèle de données?

En raison de certaines décisions de conception malchanceuses dans le passé (stockage des données sous Transformable dans un objet), je rencontre des problèmes de mémoire lors de la migration; la migration est assez difficile pour que la légèreté ne la recouvre pas, et la migration personnalisée essaie de tout charger en mémoire et échoue misérablement. Basé sur l'excellent Core Data book de Marcus Zarra, j'ai adapté son approche de la migration progressive afin de pouvoir mélanger les passes de migration successives selon une stratégie de migration légère, personnalisée ou «écrivant la vôtre». J'utilise ceci pour créer un modèle de données intermédiaire dans lequel je charge mon objet 'big data' et l'écris dans un fichier externe sur le disque, et je garde juste l'URL de ce fichier à la place.

Fondamentalement, cela ressemble à ceci:

 v1 ----(lightweight)----------> v1.5 --(lightweight)--> v2 
         |        | 
* myData: Transformable | * myData: Transformable? | * myDataUrl: String 
         | * myDataUrl: String  | 

Entre les deux migrations légères, je brancher un NSPersistentStoreController au modèle intermédiaire, aller chercher des objets qui doivent être modifiés à l'aide fetchLimit, fetchBatchSize; écrire les données dans un fichier sur le disque et annuler les données stockées dans l'objet lui-même, au cours de laquelle je enregistre régulièrement du moc et de faute les objets traités. Cela fonctionne plutôt bien ... mais ... une autre partie de la migration ne fonctionne pas très bien, où j'ai supprimé une relation et l'ai remplacée par une propriété calculée dans la classe 'Human' du fichier mogenerated. , à savoir

 v1 ----(lightweight)----------> v1.5 --(lightweight)--> v2 
           |        | 
* myRel ->> [Some object]  | * myRel ->> [Some object]? | (nothing stored here) 
                   computed property `myRel` in the `MyEntity` human class 

selon les mêmes principes, dans le « v1.5 à v1.5 » passe, je me déplace les informations stockées dans myRel à un niveau différent et d'essayer de définir la relation avec nil ensuite. L'application utilisant le datamodel v2 accède toujours aux objets référencés dans myRel en utilisant la même interface, car je l'ai ajoutée en tant que propriété calculée qui récupère les données déplacées.

public var myRel: [Some object] { return ... } 

Voici le code qui fait ce mouvement:

// Note that since I'm not working on the current data model version in this 
// so-called migration pass, I cannot/should not refer to the real `MyEntity` class that 
// mogenerator generates for me. 
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "MyEntity") 

let objects = try? moc.fetch(fetchRequest) 
objects.forEach { object in 
    // Process objects in `myRel` and move them to a different level 
    let toProcess = ($0.value(forKey: "myRel") as? NSOrderedSet)?.array as? [NSManagedObject]? 

    // ... process ... 

    // Now nullify the original relationship 
    $0.setValue(nil, forKey: "myRel") 
} 

Cette dernière ligne provoque un crash d'exécution, montrant une stacktrace qui conduit dans le mogenerator de fichier « objet » généré pour la version finale du modèle, non pour ce modèle intermédiaire

#0 0x0000000100f6885e in MyEntity.myRel.getter at ... 
#1 0x0000000100f68732 in @objc MyEntity.myRel.getter() 
#2 0x000000010ebf7db7 in _PF_Handler_Public_GetProperty() 
#3 0x000000010124f221 in NSKeyValueWillChangeBySetting() 
#4 0x0000000101249798 in NSKeyValueWillChange() 
#5 0x000000010121f618 in -[NSObject(NSKeyValueObserverNotification) willChangeValueForKey:]() 
#6 0x0000000100f47222 in NSManagedObject.setValue<A where ...> (Any?, for : A) ->() at ... 

bien entendu, ce crashes parce que le modèle de données sur lequel je travaille ne contient pas les entités visées dans ma propriété calculée myRel (ce ne sont que présents sur datamodel v2)

Cela me surprend, je construisais le NSFetchRequest surtout avec <NSManagedObject> dans l'espoir d'avoir «tranché loin la logique dynamique sous-jacente de la classe MyEntity, mais il semble de base de données/à infère Swift le type d'exécution basé sur le nom/la description de l'entité. Y a-t-il un moyen de contourner cela?

J'ai également essayé d'utiliser setPrimitiveValue, mais cela fait que mon moc manque des changements que je fais les changements que je fais à mes objets ne sont pas enregistrés de cette façon.

+0

En écrivant tout cela a aidé à voir la lumière un peu. J'ai réussi à contourner le problème en renommant 'myRel' dans le modèle de données intermédiaire en' myRelOld' et en accédant à celui-là dans la transformation intermédiaire. Comme la relation est supprimée lors de la migration vers le modèle de données v2, cela n'a pas tellement d'importance. Encore, si quelqu'un peut me donner un pointeur sur pourquoi Core Data essaye d'utiliser l'entité 'actuel datamodel' au lieu de l'intermédiaire; notez que ma propriété calculée dans le modèle le plus récent n'est pas marquée '@ dynamic' /' @ NSManaged'; ou juste montrer une lumière fraîche sur ce que je fais, toujours le bienvenu! – Stavr0s

Répondre

0

En écrivant tout cela a aidé à voir la lumière un peu. J'ai réussi à contourner le problème en renommant myRel dans le modèle de données intermédiaire à myRelOld et en accédant à celui-là dans la transformation intermédiaire. Comme la relation est supprimée lors de la migration vers le modèle de données v2, cela n'a pas tellement d'importance.