CoreData+mogenerator - Как предотвратить `setValue(forKey:)` в промежуточной модели данных для ссылки на класс "человек" для сущности в конечной модели данных?
Я использую Core Data в сочетании с mogenerator для управления довольно большим и сильно связанным графом объектов данных.
Из-за некоторых неудачных проектных решений в прошлом (хранение данных как Transformable
в объекте), я сталкиваюсь с проблемами памяти при выполнении миграции; Миграция достаточно сложна, чтобы облегчить ее, и пользовательская миграция пытается загрузить все в память и с треском проваливается.
Основываясь на превосходной книге "Основные данные " Маркуса Зарры, я адаптировал его прогрессивный подход к миграции, чтобы иметь возможность смешивать и сопоставлять последовательные этапы миграции в соответствии с упрощенной, нестандартной или "пишущей собственной" стратегией миграции. Я использую это для создания промежуточной модели данных, в которой я загружаю свой объект "больших данных" и записываю его во внешний файл на диске, а вместо этого просто сохраняю URL этого файла.
В основном это выглядит так:
v1 ----(lightweight)----------> v1.5 --(lightweight)--> v2
| |
* myData: Transformable | * myData: Transformable? | * myDataUrl: String
| * myDataUrl: String |
Между двумя легкими миграциями я подключаю NSPersistentStoreController
в промежуточную модель, выберите объекты, которые необходимо изменить, используя fetchLimit
, fetchBatchSize
; записать данные в файл на диске и обнулить данные, хранящиеся в самом объекте, во время которого я регулярно сохраняю данные moc и выполняю ошибки обработанных объектов.
Это работает довольно хорошо... но... есть другая часть миграции, которая работает не очень хорошо, где я удалил отношение и заменил его вычисляемым свойством в классе "Human" в сгенерированном файле., Т.е.
v1 ----(lightweight)----------> v1.5 --(lightweight)--> v2
| |
* myRel ->> [Some object] | * myRel ->> [Some object]? | (nothing stored here)
computed property `myRel` in the `MyEntity` human class
По тем же принципам, на проходе 'v1.5-v1.5', я перемещаю информацию, хранящуюся в myRel
на другой уровень и пытается установить отношения к nil
после этого. Приложение, использующее модель данных v2, все еще получает доступ к объектам, указанным в myRel
используя тот же интерфейс, потому что я добавил его как вычисляемое свойство, которое извлекает перемещенные данные.
public var myRel: [Some object] { return ... }
Вот код, который делает это движение:
// 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")
}
Эта последняя строка вызывает сбой во время выполнения, показывая трассировку стека, которая приводит к генератору файлов "Объект", созданному для окончательной версии модели, а не для этой промежуточной модели
#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 ...
Естественно, это происходит сбой, потому что модель данных, над которой я работаю, не содержит сущностей, указанных в моем вычисляемом свойстве myRel
(они присутствуют только в datamodel v2)
Это удивляет меня, я построил NSFetchRequest
особенно с <NSManagedObject>
в надежде "отрезать" основную динамическую логику MyEntity
класс, но, похоже, Core Data/Swift выводит тип среды выполнения на основе имени / описания объекта. Есть ли способ обойти это?
Я также пытался использовать setPrimitiveValue
, но это приводит к тому, что мой moc пропускает изменения. Я делаю изменения, которые я делаю в своих объектах, таким образом не сохраняется.
1 ответ
Записывание всего этого помогло немного увидеть свет. Мне удалось обойти проблему, переименовав myRel
в промежуточной модели данных для myRelOld
и доступ к этому в промежуточном преобразовании. Поскольку связь при миграции к модели данных v2 удаляется, это не имеет большого значения.