Подтвердить что ты не робот

IOS 5 замораживание данных ядра

Я пытаюсь выполнить следующую простую вещь:

NSArray * entities = [context executeFetchRequest:inFetchRequest error:&fetchError];

Ничего необычного. Но это зависает в iOS 5, он отлично работает в iOS 4. У меня нет исключений, предупреждений или ошибок; мое приложение просто зависает.

Пожалуйста, помогите мне! Я умираю здесь!;)

4b9b3361

Ответ 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
    }