Я использую Core Data в сочетании с mogenerator для управления довольно большим и сильно связанным графом объектов данных.
Из-за некоторых неудачных дизайнерских решений в прошлом (хранение данных как Transformable
в объекте) у меня возникают проблемы с памятью при выполнении миграции; миграция достаточно сложна, поэтому облегченная миграция не покрывает ее, а пользовательская миграция пытается загрузить все в память и терпит неудачу.
Основываясь на отличной книге Core Data Маркуса Зарры, я адаптировал свой прогрессивный подход к миграции, чтобы иметь возможность смешивать и сопоставлять последовательные проходы миграции в соответствии с облегченной, пользовательской или «написание собственной» стратегии миграции. Я использую это для создания промежуточной модели данных, в которой я загружаю свой объект «больших данных» и записываю его во внешний файл на диске, а вместо этого просто сохраняю URL-адрес этого файла.
В основном это выглядит так:
v1 ----(lightweight)----------> v1.5 --(lightweight)--> v2
| |
* myData: Transformable | * myData: Transformable? | * myDataUrl: String
| * myDataUrl: String |
В промежутке между двумя легкими миграциями я подключаю NSPersistentStoreController
к промежуточной модели, выбираю объекты, которые нужно изменить, используя fetchLimit
, fetchBatchSize
; записать данные в файл на диске и обнулить данные, хранящиеся в самом объекте, во время которых я регулярно сохраняю moc и повреждаю обработанные объекты.
Это работает довольно хорошо... но... есть еще одна часть миграции, которая работает не очень хорошо, где я удалил отношение и заменил его вычисляемым свойством в классе "Человек" сгенерированного файла, т.е.
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-to-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
(они присутствуют только в модели данных v2)
Это меня удивляет, я создал NSFetchRequest
, особенно с <NSManagedObject>
, в надежде «отрезать» базовую динамическую логику класса MyEntity
, но кажется, что Core Data/Swift определяет тип среды выполнения на основе имени/описания объекта. Есть ли способ обойти это?
Я также пытался использовать setPrimitiveValue
, но это приводит к тому, что мой moc пропускает изменения. Я делаю изменения, которые я вношу в свои объекты, не сохраняются таким образом.
myRel
в промежуточной модели данных вmyRelOld
и обратившись к ней в промежуточном преобразовании. Поскольку связь удаляется при переходе на модель данных v2, это не имеет большого значения. Тем не менее, если кто-нибудь может указать мне, почему Core Data пытается использовать объект «текущая модель данных» вместо промежуточного; обратите внимание, что мое вычисляемое свойство в новейшей модели не помечено@dynamic
/@NSManaged
; или просто показать новый свет на то, что я делаю, всегда пожалуйста! - person Stavr0s   schedule 26.04.2017