Предупреждение CoreData: несколько описаний NSEntityDescriptions претендуют на подкласс NSManagedObject

Я внезапно получаю кучу предупреждений на iOS12 / XCode 9. Почему существует несколько ManagedObjectModels? В приложении есть только один файл * .xcdatamodeld, но в модели существует несколько версий.

Это какая-то новая функция iOS12 Coredata и могу ли я что-то сделать, чтобы предотвратить это предупреждение, или мне просто игнорировать его?

2018-09-18 11:45:34.487073+1000 xxxxxxxxx[4422:1419983] [error] warning:     'Stats' (0x2812f1550) from NSManagedObjectModel (0x2806ff480) claims 'Stats'.
CoreData: warning:       'Stats' (0x2812f1550) from NSManagedObjectModel (0x2806ff480) claims 'Stats'.
2018-09-18 11:45:34.487084+1000 xxxxxxxxx[4422:1419983] [error] warning:     'Stats' (0x2812f3bd0) from NSManagedObjectModel (0x2806b18b0) claims 'Stats'.
CoreData: warning:       'Stats' (0x2812f3bd0) from NSManagedObjectModel (0x2806b18b0) claims 'Stats'.

person Duncan Groenewald    schedule 18.09.2018    source источник
comment
Что ж, очевидно, что это будет ошибка либо в вашем коде, либо в Xcode, а не функция. Apple, вероятно, провела небольшое тестирование прошлогодних сборок Xcode 9, запущенных в iOS 12 в этом году. Итак, вам следует сначала создать свой проект в Xcode 10 и соответствующим образом обновить результаты в своем вопросе.   -  person Jerry Krinock    schedule 18.09.2018
comment
Хороший момент, я не могу использовать Xcode 10 в данный момент, потому что другая библиотека, от которой я зависим, несовместима с Xcode 10.   -  person Duncan Groenewald    schedule 18.09.2018
comment
Та же проблема с использованием Xcode 10 и симулятора iOS 12   -  person Alessandro    schedule 18.09.2018
comment
DuncanGroenewald и @Alessandro: Можете ли вы подтвердить, что этого не происходит при запуске той же сборки в iOS 11?   -  person Jerry Krinock    schedule 18.09.2018
comment
@JerryKrinock - Я никогда не замечал этого раньше, и на данный момент у меня нет устройства iOS11, которое можно было бы протестировать.   -  person Duncan Groenewald    schedule 18.09.2018
comment
Этого не происходит на iOS 11 или iOS 10.   -  person Alessandro    schedule 19.09.2018
comment
Хорошо, у меня есть небольшое приложение iOS Swift Core Data, которое я только что построил и протестировал в Xcode 10. (Apple уже много лет препятствует использованию старых SDK, и похоже, что эта последняя версия Xcode не дает выбора SDK). При тестировании в симуляторе с использованием как iPhone 8 Plus, так и iPhone XS Max (оба указывают на iOS 12) мое приложение работает нормально, а в моей консоли отладки Xcode я не вижу никаких таких ошибок / предупреждений, как в вопросе Дункана. Мое приложение не основано на документах и ​​имеет одно хранилище SQLite в папке Application Support по умолчанию.   -  person Jerry Krinock    schedule 19.09.2018
comment
Итак, было бы хорошо, если бы вы, Дункан и @Alessandro, определили, что не так. Очевидный подход состоит в том, чтобы установить символические точки останова в нескольких функциях NSManagedObjectModel.init (), а затем запустить их и посмотреть, кто нажимает на них более одного раза. К сожалению, мне не удалось разрешить такую ​​точку останова. Либо я не знаю, что делаю, либо lldb все еще не очень быстр с отладкой Swift.   -  person Jerry Krinock    schedule 19.09.2018
comment
@JerryKrinock - посмотрите, что произойдет, если вы используете фоновый контекст, а также основной контекст. persistentContainer.newBackgroundContext()   -  person Duncan Groenewald    schedule 20.09.2018
comment
Кажется, это связано с загрузкой модели с диска несколько раз. Не уверен, что это новое поведение (например, у Apple есть ошибка в кешировании / уникальности / деинициализации модели) или это просто новое предупреждение. Кажется, это можно исправить путем перехода с NSManagedObject.init(context:) на init(entity:insertInto:), потому что код будет выглядеть в конкретной модели. Но я этого не делаю. Поведение ограничено моими тестами (я не загружаю модели несколько раз в приложение), и других проблем нет, поэтому я пока просто проигнорирую это. YMMV.   -  person Matthias Bauch    schedule 20.11.2018


Ответы (3)


Я только что решил ту же ошибку, когда использовал вычисляемое свойство для своего постоянного контейнера. Таким образом, каждый раз, когда приложение обращается к постоянному контейнеру / хранилищу, с диска создается новый экземпляр модели данных.

После того, как я изменил постоянный контейнер на ленивое сохраненное свойство, проблема исчезла.

[Обновить]

В настоящее время я использую отдельный класс для основного стека данных, в котором используется синглтон, как показано ниже:

class DataCtrl : NSObject {

    static shared = DateCtrl()
    var container: NSPersistentContainer?
    
    private override init() { 
        container = NSPersistentContainer(name: "dataModelName")
    }

    func loadStore(completionHandler: @escaping () -> ()) {
        self.container?.loadPersisentStores() { desc, err in ...
            completionHandler
        }
    }
}

Тогда я могу с комфортом использовать вычисляемое свойство в расширении tableViewController:

var container : persistentContainer { return DateCtrl.shared.container }

конечно, вам нужно вызвать func loadStore в блоке AppDelegate didFinishLaunchingWithOptions, чтобы сначала загрузить постоянное хранилище, где используется DispatchGroup () в завершенииHandler для управления загрузкой модели данных первого контроллера представления.

person Ron Liu    schedule 22.10.2018

Я понял, как это решить. Вы должны создать один экземпляр NSEntityDescription в своем классе модульного теста и повторно использовать его каждый раз, когда вы хотите создать объект, который соответствует описанию сущности. В приведенном ниже коде посмотрите на setup, tearDown и testFetchPerson ().

   var container: NSPersistentContainer!

   **var entityDescription: NSEntityDescription! // Insert only one instance into your managed object context * see setup//**

   override func setUp() {
       super.setUp()
       let bundle = Bundle(for: type(of: self))
       let url = bundle.url(forResource: "DivyaPracticeWorkspace", withExtension: "momd")!
       let managedObjectModel = NSManagedObjectModel(contentsOf: url)!
       container = NSPersistentContainer(name: "testContainer", managedObjectModel: managedObjectModel)
       let description = NSPersistentStoreDescription()
       description.type = NSInMemoryStoreType
       container.persistentStoreDescriptions = [description]
       container.loadPersistentStores(completionHandler: { (description, error) in
           if let error = error {
               print("\(error)")
           }
       })

       mockCoreData = CoreDataManager(container: container)
**// Insert only one entity description into the context
       entityDescription  = NSEntityDescription.entity(forEntityName: "Person", in: mockCoreData.mainContext)!**
   }


   override func tearDown() {
       super.tearDown()
       deleteAllObjects()
       mockCoreData = nil
       container = nil
   }

   private func deleteAllObjects() {
       let request: NSFetchRequest<Person> = Person.fetchRequest()
       do {
           let results = try mockCoreData.mainContext.fetch(request)
           for entry in results {
               mockCoreData.mainContext.delete(entry)
           }

           mockCoreData.saveChanges {
               [weak self] in
               guard let this = self else {
                   return
               }

               this.mockCoreData.mainContext.refreshAllObjects()
               this.mockCoreData.mainContext.reset()
               guard let store = this.container.persistentStoreCoordinator.persistentStores.first else {
                   return
               }
               do {
                   try this.container.persistentStoreCoordinator.remove(store)
               } catch let error {
                   print("\(error)")
               }
           }
       } catch let error {
           print("\(error)")
       }
   }

   func testFetchPerson() {
        var fetchSuccess = false
**// Create person entity using the entity description created in setup.
           let personEntity = Person(entity: entityDescription, insertInto: mockCoreData.mainContext)**

           personEntity.name = "Bob"
           personEntity.gender = Int32(0)

           mockCoreData.saveChanges(completion: nil)


           if let personFetched = mockCoreData.fetchPerson(name: "Bob")?.first {
               fetchSuccess = personFetched.gender == Int32(0) && personFetched.name == "Bob"
           }
       XCTAssertTrue(fetchSuccess)
   }
person Diviyo    schedule 23.05.2019

Если каждый раз, когда вы хотите получить доступ к своему постоянному контейнеру, вы создаете экземпляр NSPersistentContainer (name :), вы получите это предупреждение. У меня возникла эта проблема, потому что я использовал два разных контейнера в своем классе хранения (каждый контейнер для другой модели управляемого объекта). Я решил это, сохранив каждый контейнер в свойстве var, и всякий раз, когда я меняю постоянный контейнер, я переназначаю его из свойства, не создавая другой экземпляр следующим образом:

var mainPersistentContainer: NSPersistentContainer?
var firstContainer : NSPersistentContainer?
var secondContainer: NSPersistentContainer?

А затем, когда вы запускаете контейнер, попробуйте назначить его своей переменной

if firstContainerCondition {
if firstContainer != nil {
mainPersistentContainer = firstContainer
} else {
firstContainer = NSPersistentContainer(name: "firstMangedObjectModel")
}

То же самое и со вторым контейнером.

person Karrar    schedule 20.10.2020