Загруженные Restkit вложенные сущности Core Data вызывают исключение NSObjectInaccessibleException - PullRequest
6 голосов
/ 12 января 2012

Я использую RestKit для извлечения объектов из моего сервиса RoR и использую CoreData для сохранения некоторых объектов (больше объектов таблицы поиска статического типа). TasteTag является одним из таких сохраняемых объектов:

#ifdef RESTKIT_GENERATE_SEED_DB
    NSString *seedDatabaseName = nil;
    NSString *databaseName = RKDefaultSeedDatabaseFileName;
#else
    NSString *seedDatabaseName = RKDefaultSeedDatabaseFileName;
    NSString *databaseName = @"Model.sqlite";
#endif

RKObjectManager* manager = [RKObjectManager objectManagerWithBaseURL:kServerURL];  
manager.objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:databaseName usingSeedDatabaseName:seedDatabaseName managedObjectModel:nil delegate:self];

.. lots of fun object mapping ..

 RKManagedObjectMapping* tasteTagMapping = [RKManagedObjectMapping mappingForClass:[TasteTag class]];
[tasteTagMapping mapKeyPath:@"id" toAttribute:@"tasteTagID"];
[tasteTagMapping mapKeyPath:@"name" toAttribute:@"name"];
tasteTagMapping.primaryKeyAttribute = @"tasteTagID";
[[RKObjectManager sharedManager].mappingProvider setMapping:tasteTagMapping forKeyPath:@"taste_tags"]; 
[[RKObjectManager sharedManager].mappingProvider addObjectMapping:tasteTagMapping];

.. some more mapping ..

У меня есть данные, возвращаемые с сервера RoR, и они отображаются на объекты, как и ожидалось. После того, как RestKit получит запрос обратно, сущность Core Data также будет отображаться нормально:

"<TasteTag: 0x6e87170> (entity: TasteTag; id: 0x6e85d60 <x-coredata://03E4A20A-21F2-4A2D-92B4-C4424893D559/TasteTag/p5> ; data: <fault>)"

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

for (TasteTag *tag in self.vintage.tasteTags) {
    [tagNames addObject:tag.name]; //get error of trying to add nil to array   
}

После изучения ошибок, вызванных вручную (http://www.mlsite.net/blog/?p=518) Я попытался вызвать [tag willAccessValueForKey:nil], что привело к:

Terminating app due to uncaught exception 'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0x6e7b060 <x-coredata://03E4A20A-21F2-4A2D-92B4-C4424893D559/TasteTag/p5>''

Поиск сущности в .sqlite на основе ключа (TasteTag / p5) показывает, что он сопоставлен с тем, который я ожидал.

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

Если я просто позвоню [TasteTag allObjects], я смогу вернуть все объекты обратно, и они загрузятся без проблем. Это как раз в том случае, если их обвиняют, кажется.

Ответы [ 2 ]

10 голосов
/ 16 января 2012

Я нашел решение, которое сработало для меня (я не уверен, насколько оно применимо к вашей ситуации, но я добавляю его в качестве ответа, поскольку оно решило эту (или очень похожую) проблему для меня):

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

Я разделю это на куски, чтобы попытаться помочь вам следовать моему образу мышления в то время (поскольку я не думаю, что наши проблемы идентичны).

Мое исходное предположение о реализации

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

Я был не прав

Я заметил, что код RKTwitterCoreData действительно не работает таким образом в меньшей степени. Приличный кусок моего кода совпал с их кодом, но самым большим отличием было то, что они не рассматривали эти объекты как взаимозаменяемые. Фактически, они никогда не использовали объекты, полученные из удаленных хранилищ данных. Вместо этого они просто позволяют этому «провалиться сквозь щели». Я могу только предположить, что это означает, что они добавлены в хранилище данных Core Data, поскольку оно работает для них, а теперь и для меня.

Детали

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

Для уточнения, если вы посмотрите на RKTwitterViewController, вы заметите, что строки 45-61 обрабатывают загрузку объектов:

- (void)loadObjectsFromDataStore {
    [_statuses release];
    NSFetchRequest* request = [RKTStatus fetchRequest];
    NSSortDescriptor* descriptor = [NSSortDescriptor sortDescriptorWithKey:@"createdAt" ascending:NO];
    [request setSortDescriptors:[NSArray arrayWithObject:descriptor]];
    _statuses = [[RKTStatus objectsWithFetchRequest:request] retain];
}

- (void)loadData {
    // Load the object model via RestKit    
    RKObjectManager* objectManager = [RKObjectManager sharedManager];
    [objectManager loadObjectsAtResourcePath:@"/status/user_timeline/RestKit" delegate:self block:^(RKObjectLoader* loader) {
        // Twitter returns statuses as a naked array in JSON, so we instruct the loader
        // to user the appropriate object mapping
        loader.objectMapping = [objectManager.mappingProvider objectMappingForClass:[RKTStatus class]];
    }];
}

Все выглядит нормально (по крайней мере по сравнению с тем, как я делал эту загрузку изначально). Но взгляните на метод делегата objectLoader:didLoadObjects::

- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {
    [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:@"LastUpdatedAt"];
    [[NSUserDefaults standardUserDefaults] synchronize];
    NSLog(@"Loaded statuses: %@", objects);
    [self loadObjectsFromDataStore];
    [_tableView reloadData];
}

Образец даже не касается параметра objects! (Кроме NSLog конечно ...)

Заключение / Т.Л.; др

Не используйте управляемые объекты, которые вы возвращаете в objectLoader:didLoadObjects:, как если бы они полностью обеспечивались Базовыми данными. Вместо этого игнорируйте их и повторно извлекайте данные из Core Data. Все объекты, в том числе и из последнего запроса, находятся там. В противном случае вы получите неисправимые ошибки (по крайней мере, я).

4 голосов
/ 16 января 2012

Документирование моего исправления (читай: хак) в соответствии с предложением Райана.

Кажется, ошибка в том, как RestKit предположил, что вы будете использовать объекты, возвращенные из их метода objectLoader:didLoadObjects:.Похоже, они предполагают, что все они будут поддерживаться Core Data (и будут следовать потоку, аналогичному тому, о котором говорил Райан - пусть он синхронизируется с Core Data, а затем повторно запрашивать), или что вы будете использовать все объекты без поддержки Core Data и простодержите эти результаты рядом.

В моем случае у меня был микс - корневой массив объектов без поддержки Core Data, каждый из которых затем содержал массив объектов с поддержкой Core Data.К объектам верхнего уровня я обращаю внимание на запросы к серверу, и у меня нет оснований для локального сохранения за пределами представления, в котором они показаны. Кажется, как только objectLoader:didLoadObjects: завершает контекст управляемого объекта, поддерживая сущности базовых данных внутрипараметр objects удаляется (в предположении, что вы будете запрашивать его у него), в результате чего любые будущие вызовы к объектам будут рассматриваться как ошибки, даже если вы не можете вызвать ошибку и загрузитьdata (приводит к NSObjectInaccessibleException).

Я обошелся с этим безобразным хаком - в objectLoader:didLoadObjects: я получаю доступ к одному из контекста управляемого объекта сущности Core Data и копирую его в свойство в представлении (self.context = [tag managedObjectContext];).Это предотвращает освобождение контекста после того, как objectLoader:didLoadObjects: завершено, что позволяет мне получить доступ к объектам без проблем позже в представлении.

Другое решение состояло бы в том, чтобы вручную повторно запросить для каждого объекта, используя новый контекст, ископируйте это обратно в сохраненные возвращаемые объекты.Это можно сделать, когда нужно их отобразить, или, возможно, выполнить некоторую постобработку в objectLoader:didLoadObjects:, используя новый контекст.Идентификатор объекта все еще присутствует в поврежденном объекте, поэтому его можно использовать для повторного запроса без проблем даже после исчезновения исходного контекста RestKit.Но кажется глупым повторный запрос для каждой сущности в графе объектов, подобный этому.

...