CoreData+mogenerator - Как запретить `setValue(forKey:)` в промежуточной модели данных ссылаться на «человеческий» класс для объекта в окончательной модели данных?

Я использую 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 пропускает изменения. Я делаю изменения, которые я вношу в свои объекты, не сохраняются таким образом.


person Stavr0s    schedule 25.04.2017    source источник
comment
Записав все это, я немного прозрел. Мне удалось обойти проблему, переименовав myRel в промежуточной модели данных в myRelOld и обратившись к ней в промежуточном преобразовании. Поскольку связь удаляется при переходе на модель данных v2, это не имеет большого значения. Тем не менее, если кто-нибудь может указать мне, почему Core Data пытается использовать объект «текущая модель данных» вместо промежуточного; обратите внимание, что мое вычисляемое свойство в новейшей модели не помечено @dynamic/@NSManaged; или просто показать новый свет на то, что я делаю, всегда пожалуйста!   -  person Stavr0s    schedule 26.04.2017


Ответы (1)


Записав все это, я немного прозрел. Мне удалось обойти проблему, переименовав myRel в промежуточной модели данных в myRelOld и обратившись к ней в промежуточном преобразовании. Поскольку связь удаляется при переходе на модель данных v2, это не имеет большого значения.

person Stavr0s    schedule 26.04.2017