IOS 5 замораживание данных ядра
Я пытаюсь выполнить следующую простую вещь:
NSArray * entities = [context executeFetchRequest:inFetchRequest error:&fetchError];
Ничего необычного. Но это зависает в iOS 5, он отлично работает в iOS 4. У меня нет исключений, предупреждений или ошибок; мое приложение просто зависает.
Пожалуйста, помогите мне! Я умираю здесь!;)
Ответы
Ответ 1
Спасибо за подсказки, приведенные на этой странице, о том, как решить эту проблему замораживания, которая появилась при обновлении с iOS4. Это была самая неприятная проблема, которую я нашел с тех пор, как начал программирование на iOS.
Я нашел быстрое решение для случаев, когда есть только несколько вызовов контекста из других потоков.
Я просто использую выполнитьSelectorOnMainThread:
[self performSelectorOnMainThread:@selector(stateChangeOnMainThread:) withObject: [NSDictionary dictionaryWithObjectsAndKeys:state, @"state", nil] waitUntilDone:YES];
Чтобы определить места, где вызван контекст из другого потока, вы можете поместить точку останова в NSLog в функции, где вы вызываете контекст, как в следующем фрагменте кода, и просто используйте выполнитьSelectorOnMainThread на их.
if(![NSThread isMainThread]){
NSLog(@"Not the main thread...");
}
Я надеюсь, что это может быть полезно...
Ответ 2
Я не знаю, пользуетесь ли вы другой Thread. Если да, проблема возникает из-за того, что сами NSManagedObject не являются потокобезопасными. Создание ManagedContext в основном потоке и использование его в другом потоке замораживает поток.
Возможно, эта статья может вам помочь:
http://www.cimgf.com/2011/05/04/core-data-and-threads-without-the-headache/
Apple имеет демонстрационное приложение для обработки Coredata по нескольким потокам (обычно основным и фоновым потокам): http://developer.apple.com/library/ios/#samplecode/TopSongs/Introduction/Intro.html
Что я сделал для решения этой проблемы:
- В делетете приложения: создайте постоянное хранилище (одно для всего потока) и создайте управляемый Coredata контекст для основного потока,
- В фоновом потоке создайте новый управляемый контекст (из одного и того же постоянного хранилища)
- Уведомления сохраняются, чтобы знать mainContext, когда фоновый поток завершен (вставка строк или другой).
Существует несколько решений, использующих NSQueueOperation. В моем случае я работаю с циклом while. Вот мой код, если он может вам помочь. Тем не менее, документация Apple на concurrency и пример приложения Top Songs - хорошие моменты для запуска.
в делетете приложения:
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
self.cdw = [[CoreDataWrapper alloc] initWithPersistentStoreCoordinator:[self persistentStoreCoordinator] andDelegate:self];
remoteSync = [RemoteSync sharedInstance];
...
[self.window addSubview:navCtrl.view];
[viewController release];
[self.window makeKeyAndVisible];
return YES;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator == nil) {
NSURL *storeUrl = [NSURL fileURLWithPath:self.persistentStorePath];
NSLog(@"Core Data store path = \"%@\"", [storeUrl path]);
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[NSManagedObjectModel mergedModelFromBundles:nil]];
NSError *error = nil;
NSPersistentStore *persistentStore = [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error];
NSAssert3(persistentStore != nil, @"Unhandled error adding persistent store in %s at line %d: %@", __FUNCTION__, __LINE__, [error localizedDescription]);
}
return persistentStoreCoordinator;
}
-(NSManagedObjectContext *)managedObjectContext {
if (managedObjectContext == nil) {
managedObjectContext = [[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator:self.persistentStoreCoordinator];
}
return managedObjectContext;
}
-(NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator == nil) {
NSURL *storeUrl = [NSURL fileURLWithPath:self.persistentStorePath];
NSLog(@"Core Data store path = \"%@\"", [storeUrl path]);
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[NSManagedObjectModel mergedModelFromBundles:nil]];
NSError *error = nil;
NSPersistentStore *persistentStore = [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error];
NSAssert3(persistentStore != nil, @"Unhandled error adding persistent store in %s at line %d: %@", __FUNCTION__, __LINE__, [error localizedDescription]);
}
return persistentStoreCoordinator;
}
-(NSManagedObjectContext *)managedObjectContext {
if (managedObjectContext == nil) {
managedObjectContext = [[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator:self.persistentStoreCoordinator];
}
return managedObjectContext;
}
-(NSString *)persistentStorePath {
if (persistentStorePath == nil) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths lastObject];
persistentStorePath = [[documentsDirectory stringByAppendingPathComponent:@"mgobase.sqlite"] retain];
}
return persistentStorePath;
}
-(void)importerDidSave:(NSNotification *)saveNotification {
if ([NSThread isMainThread]) {
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:saveNotification];
} else {
[self performSelectorOnMainThread:@selector(importerDidSave:) withObject:saveNotification waitUntilDone:NO];
}
}
В объекте, выполняющем фоновый поток:
monitor = [[NSThread alloc] initWithTarget:self selector:@selector(keepMonitoring) object:nil];
-(void)keepMonitoring{
while(![[NSThread currentThread] isCancelled]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
AppDelegate * appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
//creating the cdw here will create also a new managedContext on this particular thread
cdwBackground = [[CoreDataWrapper alloc] initWithPersistentStoreCoordinator:appDelegate.persistentStoreCoordinator andDelegate:appDelegate];
...
}
}
Надеюсь на эту помощь,
М.
Ответ 3
У меня была такая же проблема. Если вы запустите под отладчиком и когда приложение "зависает", остановите приложение (используйте кнопку "пауза" в отладчике. Если вы находитесь в строке executeFetchRequest, проверьте переменную контекста. Если у нее есть ivar _objectStoreLockCount и ее больше 1, затем его ожидание блокировки в соответствующем хранилище.
Где-то вы создаете условие гонки в своем связанном хранилище.
Ответ 4
Это действительно похоже на попытку доступа к NSManagedObjectContext
из потока/очереди, отличного от того, который его создал. Как и другие, вам необходимо посмотреть на вашу потоковую обработку и убедиться, что вы выполняете правила основных данных.
Ответ 5
Выполнение запроса выборки должно происходить из потока, в котором был создан контекст.
Помните, что это не потокобезопасно, и попытка executeFetchRequest
из другого потока приведет к непредсказуемому поведению.
Чтобы сделать это правильно, используйте
[context performBlock: ^{
NSArray * entities = [context executeFetchRequest:inFetchRequest error:&fetchError];
}];
Это будет executeFetchRequest
в том же потоке, что и контекст, который может быть или не быть основным потоком.
Ответ 6
В моем случае приложение будет замораживаться до 'executeFetchRequest' без предупреждения. Решение заключалось в том, чтобы обернуть все операции db в @synchronized (persistentStore).
Например:
NSArray *objects;
@synchronized([self persistentStoreCoordinator]) {
objects = [moc executeFetchRequest:request error:&error];
}
Ответ 7
Удалить все объекты с помощью fetchrequest для меня не работает, sqlite выглядит поврежденным. единственный способ, которым я нашел, -
//Erase the persistent store from coordinator and also file manager.
NSPersistentStore *store = [self.persistentStoreCoordinator.persistentStores lastObject];
NSError *error = nil;
NSURL *storeURL = store.URL;
[self.persistentStoreCoordinator removePersistentStore:store error:&error];
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:&error];
//Make new persistent store for future saves (Taken From Above Answer)
if (![self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
// do something with the error
}