Загадочная ошибка из базовых данных: NSInvalidArgumentException, причина: referenceData64 определена только для абстрактного класса - PullRequest
31 голосов
/ 06 января 2010

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

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

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

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

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

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

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

Ответы [ 6 ]

30 голосов
/ 06 января 2010

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

EDIT:

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

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

`

 - (void)onFinishParsing {  
  // lock the store we share with main thread's 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];  
  }  
}  

`

12 голосов
/ 24 апреля 2013

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

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

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

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

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

Надеюсь, этот ответ кому-нибудь поможет!

2 голосов
/ 03 июня 2016

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

[context performBlockAndWait:^{

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

}];
2 голосов
/ 09 января 2010

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

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

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

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

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

1 голос
/ 10 февраля 2012

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

Привет

1 голос
/ 07 января 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...