Cryptic error из Core Data: NSInvalidArgumentException, причина: referenceData64 определен только для абстрактного класса

Я делаю приложение iPhone, которое считывает данные из XML файла, превращает их в управляемые объекты Core Data и сохраняет их.

Приложение работает отлично, в основном, на меньшем наборе данных /XML, который содержит ~ 150 объектов. Я сказал в основном, что в 10% случаев я получаю следующее исключение от CoreData при попытке сохранить контекст:

* Завершение приложения из-за неперехваченного исключения "NSInvalidArgumentException", причина: '* -_referenceData64, определенная только для абстрактного класса. Определить - [NSTemporaryObjectID_default _referenceData64]!

В большем наборе данных (~ 2000) это происходит каждый раз, но не на том же месте. Это может потерпеть неудачу на 137-м альбоме, 580-м или самом последнем. Я попытался переместить точку сохранения (за объект, на 10 объектов, сохранить, как только все объекты будут выделены /init 'ed), но я всегда ударил исключение выше.

Я искал исключение и увидел, что кто-то имеет те же проблемы, но не видит никаких разрешений.

Мой следующий шаг должен был упростить управляемые объекты и отношения до точки, где эта ошибка останавливается и строится оттуда, чтобы изолировать проблему. Последнее средство - вырезать Core Data и просто хранить в sqllite.

Спасибо за вашу помощь!

Ответ 1

У меня такая же проблема. Он работает для небольших наборов данных, но для больших наборов я получаю ошибки "_referenceData64, определенные только для абстрактного класса". В моей модели нет абстрактных объектов.

EDIT:

Думаю, я решил это. Проблема в моем случае была путаницей с моей стороны. Вот рекомендации, которые я выполнил, чтобы исправить это:

  • Я анализирую XML-данные в потоке. При запуске этого потока создайте новый NSManagedObjectContext, используя тот же постоянный координатор хранилища, что и ваш основной поток NSManagedObjectContext.
  • Любые новые объекты, которые вы делаете в своем потоке, должны быть сделаны для потока NSManagedObjectContext. Если вам нужно скопировать объекты из основного потока NSManagedObjectContext, скопируйте его по идентификатору. Т.е.
    NSManagedObjectID *objectID = [foo objectID];
    FooClass *newFoo = [(FooClass*)[threadManagedObjectContext objectWithID:objectID] retain]
  • Когда закончите синтаксический анализ, вам нужно сохранить изменения, внесенные в поток NSManagedObjectContext. Вы должны заблокировать постоянный координатор магазина. Я использовал следующий (неполный код):

`

 - (void)onFinishParsing {  
  // lock the store we share with main thread context  
  [persistentStoreCoordinator lock];  

  // save any changes, observe it so we can trigger merge with the actual context  
  @try {  
    [threadManagedObjectContext processPendingChanges];  
  }  
  @catch (NSException * e) {  
    DLog(@"%@", [e description]);  
    [persistentStoreCoordinator unlock];  
  }  
  @finally {  
    // pass  
  }  

  NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];  
  [dnc addObserver:self selector:@selector(threadControllerContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:threadManagedObjectContext];  
  @try {  
    NSError *error;  
    if (![threadManagedObjectContext save:&error]) {  
      DLog(@"%@", [error localizedDescription]);  
      [persistentStoreCoordinator unlock];  
      [self performSelectorOnMainThread:@selector(handleSaveError:) withObject:nil waitUntilDone:NO];  
    }  
  } @catch (NSException *e) {  
    DLog(@"%@", [e description]);  
    [persistentStoreCoordinator unlock];  
  } @finally {  
    // pass  
  }  
  [dnc removeObserver:self name:NSManagedObjectContextDidSaveNotification object:threadManagedObjectContext];  

  [self performSelectorOnMainThread:@selector(parserFinished:) withObject:nil waitUntilDone:NO];  
}  

// Merging changes causes the fetched results controller to update its results  
- (void)threadControllerContextDidSave:(NSNotification*)saveNotification {  
  // need to unlock before we let main thread merge  
  [persistentStoreCoordinator unlock];  
  [self performSelectorOnMainThread:@selector(mergeToMainContext:) withObject:saveNotification waitUntilDone:YES];  
}  

- (void)mergeToMainContext:(NSNotification*)saveNotification {  
  NSError *error;  
  [managedObjectContext mergeChangesFromContextDidSaveNotification:saveNotification];  
  if (![managedObjectContext save:&error]) {  
    DLog(@"%@", [error localizedDescription]);  
    [self handleSaveError:nil];  
  }  
}  

`

Ответ 2

Вы должны следовать правилу:

NSManagedObjectContext должен быть создан в том же потоке, который использует Это. (ИЛИ, другими словами, каждый поток должен иметь свой собственный MOC)

Нарушение вышеприведенного правила вызывает следующее исключение:

  • Исключение в *** -_referenceData64 определено только для абстрактного класса. Определить - [NSTemporaryObjectID_default _referenceData64]!,

Другая проблема, с которой может столкнуться человек, заключается в том, что при использовании NSFetchedResultsController делегаты не будут вызываться в классах пользовательского интерфейса.

Я надеюсь, что этот ответ поможет кому-то!

Ответ 3

Сделайте свое отображение данных nsmanagedobject и сохраните managedobjectcontext в следующем блоке, чтобы он блокировал доступ managedobjectcontext к другому потоку и разрешил сбой.

[context performBlockAndWait:^{

    //your code
    [context save:&error];

}];

Ответ 4

Извините за мой английский (я французский). У меня была такая же проблема, и я понял, что я вызвал метод в Core Data Framework (вставка объекта) из второго потока. Я просто вызываю этот метод из основного потока, используя функцию performSelectorOnMainThread, и это разрешает мою проблему. Надеюсь, это поможет вам.

Ответ 5

Спасибо всем, я смог избавиться от этого отвратительного исключения, следуя советам.

Это была проблема с потоками, которая, по-видимому, вызывала исключение. В моем случае у меня был основной поток, создающий рабочие потоки, которые извлекают XML, анализируют, создают необходимый управляемый объект и сохраняют их.

Я попробовал ленивый выход, используя функцию performSelectorOnMainThread для сохранения, но это не сработало.

Мой последний подход заключался в создании класса ThreadDataService с его собственным ManagedObjectContext, и каждый поток имеет один экземпляр ThreadDataService, в основном то, что предложил Адриаан.

Опять же, спасибо за все ответы. Вы, ребята, рок!

Ответ 6

У меня была такая же проблема, и при поиске ответа я нашел это. Моя проблема состояла в том, что я запустил 2 потока, которые работали в одном управляемом контексте, разбился при сохранении в постоянном хранилище, если в каждом потоке есть собственный контекст, проблема не возникает. Но это могло быть разрешено путем блокировки постоянного хранилища, но я считаю, что 2 управляемых контекста - правильное решение.

Привет