В настоящее время я разрабатываю приложение, которое использует CoreData и NSFectchedResultsController. Это приложение содержит только один UITableView, который использует NSFetchedResultsController.
1 / При запуске приложения отключается другой поток. В этом новом потоке вызов WS позволяет получать данные с веб-сервера. После вызова WS я сохраняю данные в своей базе данных CoreData с другим NSManagedObjectContext (лучшая практика Apple: Другой поток => Другой контекст). Я должен удалить все объекты этого объекта перед сохранением новых объектов. Я объединяю этот другой контекст с основным контекстом через mergeChangesFromContextDidSaveNotification.
// Data Manager (in another thread)
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(contextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:context];
[context setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
[context setPersistentStoreCoordinator:[self getPersistentStoreCoordinator]];
...
for (NSManagedObject * obj in objects)
{
[context deleteObject:obj];
}
...
for(NSDictionary *serverObj in serverObjects)
{
objAd = [NSEntityDescription
insertNewObjectForEntityForName:@"MyEntity"
inManagedObjectContext:context];
...
}
[context save:&error];
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:context];
[context release];
...
- (void)contextDidSave:(NSNotification *)notification
{
SEL selector = @selector(mergeChangesFromContextDidSaveNotification:);
[[self getContext] performSelectorOnMainThread:selector withObject:notification waitUntilDone:YES];
}
- (NSManagedObjectContext *) getContext
{
return [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
- (NSPersistentStoreCoordinator *) getPersistentStoreCoordinator
{
return [(AppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator];
}
2 / Вот мой геттер NSFectchedResultsController:
// UIView
- (NSFetchedResultsController*) offersFRC {
if (offersFRC == nil)
{
NSManagedObjectContext *l_ManagedObjectContext = [[DataManager sharedDataManager] getContext];
NSFetchRequest *l_FetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *l_Entity = [NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:l_ManagedObjectContext];
[l_FetchRequest setEntity:l_Entity];
[l_FetchRequest setFetchBatchSize:5];
NSNumber *sortType = [self.searchCriterions objectForKey:@"sortType"];
NSSortDescriptor *l_SortDescriptor = [[NSSortDescriptor alloc] initWithKey:[Constants getFieldNameBySortType:sortType] ascending:[Constants isAscendingBySortType:sortType]];
[l_FetchRequest setSortDescriptors:[NSArray arrayWithObjects:l_SortDescriptor, nil]];
[l_SortDescriptor release];
NSFetchedResultsController *l_FetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:l_FetchRequest managedObjectContext:l_ManagedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
[l_FetchRequest release];
[self setOffersFRC:l_FetchedResultsController];
[l_FetchedResultsController release],l_FetchedResultsController = nil;
[self.offersFRC setDelegate:self];
}
return offersFRC;
}
3 / У меня при запуске приложения возникает следующая ошибка:
2012-02-29 11:56:09.119 Nanopost[1996:207] *** Terminating app due to uncaught exception 'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0x5c3c760 <x-coredata://E176B0A1-275B-4332-9231-49FD88238C2B/Ads/p231>''
*** Call stack at first throw:
(
0 CoreFoundation 0x02bfe919 __exceptionPreprocess + 185
1 libobjc.A.dylib 0x02e595de objc_exception_throw + 47
2 CoreData 0x028b833f _PFFaultHandlerLookupRow + 1407
3 CoreData 0x028b5ee3 _PF_FulfillDeferredFault + 499
4 CoreData 0x028b9f3f _sharedIMPL_pvfk_core + 95
5 CoreData 0x0292a010 _PF_Handler_Public_GetProperty + 160
6 Foundation 0x02442c4f -[NSSortDescriptor compareObject:toObject:] + 128
7 CoreData 0x0297db5e +[NSFetchedResultsController(PrivateMethods) _insertIndexForObject:inArray:lowIdx:highIdx:sortDescriptors:] + 286
8 CoreData 0x0297e1b2 -[NSFetchedResultsController(PrivateMethods) _postprocessInsertedObjects:] + 402
9 CoreData 0x029841bc -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] + 1804
10 Foundation 0x02380c1d _nsnote_callback + 145
11 CoreFoundation 0x02bd6cf9 __CFXNotificationPost_old + 745
12 CoreFoundation 0x02b5611a _CFXNotificationPostNotification + 186
13 Foundation 0x023767c2 -[NSNotificationCenter postNotificationName:object:userInfo:] + 134
14 CoreData 0x028c0519 -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] + 89
15 CoreData 0x028f802b -[NSManagedObjectContext mergeChangesFromContextDidSaveNotification:] + 1579
16 Foundation 0x02395e9a __NSThreadPerformPerform + 251
17 CoreFoundation 0x02bdfd7f __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
18 CoreFoundation 0x02b3e2cb __CFRunLoopDoSources0 + 571
19 CoreFoundation 0x02b3d7c6 __CFRunLoopRun + 470
20 CoreFoundation 0x02b3d280 CFRunLoopRunSpecific + 208
21 CoreFoundation 0x02b3d1a1 CFRunLoopRunInMode + 97
22 GraphicsServices 0x031e62c8 GSEventRunModal + 217
23 GraphicsServices 0x031e638d GSEventRun + 115
24 UIKit 0x0063cb58 UIApplicationMain + 1160
25 Nanopost 0x0000230a main + 170
26 Nanopost 0x00002255 start + 53
)
terminate called after throwing an instance of '_NSCoreDataException'
Важные примечания:
- Вылетает только на iOS4
- controllerWillChangeContent - это последняя функция, вызываемая в моем коде перед сбоем приложения. controllerDidChangeContent / didChangeObject / didChangeSection не вызываются.
- Когда я комментирую [l_FetchRequest setFetchBatchSize: 5] => Больше никаких сбоев
- Когда я добавляю [context save: & error] после удаления объектов и перед вставкой новых объектов => Больше никаких сбоев
- Когда я использую [l_FetchRequest setFetchBatchSize: 24] => Сбои
- Когда я использую [l_FetchRequest setFetchBatchSize: 25] => Больше никаких сбоев
Я потратил много времени, пытаясь разобраться в этой проблеме, поэтому заранее большое спасибо за ваши ответы!
Томас
Редактировать 1 (@Jody): Привет, Джоди, большое спасибо за ваши ответы!
Вот код, используемый для обработки contextDidSave:
- (void)contextDidSave:(NSNotification *)notification
{
SEL selector = @selector(mergeChangesFromContextDidSaveNotification:);
[[self getContext] performSelectorOnMainThread:selector withObject:notification waitUntilDone:YES];
}
"сначала вы должны рассказать мне больше о контексте, который вы используете":
В этом приложении я использую 2 контекста:
№ 1: Создается по умолчанию в AppDelegate при создании XCodeProject. Этот контекст используется моим FRC и позволяет отображать строки UITableView.
№2: Созданный в моем «DataManager» (первый блок кода в моем сообщении) синглтон, который позволяет обновлять мою БД (вызов WS, удаление, повторная вставка, сохранение).
При сохранении контекста № 2 вызывается contextDidSave, чтобы объединить этот контекст с основным контекстом (контекст № 1). После этого вызывается метод моего делегата FRC «controllerWillChangeContent». Я не думаю, что это поможет показать код, содержащийся в этом методе, потому что даже если я просто поставлю NSLog, он выйдет из строя после этого метода (я поместил много NSLog, и NSLog, содержащийся в controllerWillChangeContent, будет последним, который отображается перед авария).
Я разместил сообщение на форуме Apple Dev Forum, и есть интересный ответ: https://devforums.apple.com/thread/152172?tstart=0
Редактировать 2 (@Jody): Привет, Джоди!
Как вы можете видеть в следующем методе, мой FRC не использует MOC из другого потока:
- (NSManagedObjectContext *) getContext
{
return [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
Этот метод моего "DataManager" возвращает MOC AppDelegate (= MOC основного потока)