iOS 5 Core Data заморозить - PullRequest
       4

iOS 5 Core Data заморозить

26 голосов
/ 18 октября 2011

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

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

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

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

Ответы [ 7 ]

19 голосов
/ 18 октября 2011

Я не знаю, если вы также используете другую тему. Если да, то проблема заключается в том, что сами NSManagedObjects не являются потокобезопасными. Создание 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 по параллелизму и пример приложения 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];
        ...
    }
}

Надеюсь, эта помощь,

* 1 028 * М.
11 голосов
/ 22 октября 2011

Спасибо за подсказки, приведенные на этой странице, о том, как решить эту проблему с зависанием, которая появилась при обновлении с iOS4. Это самая раздражающая проблема, которую я обнаружил с тех пор, как начал программировать на iOS.

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

Я просто использую executeSelectorOnMainThread :

    [self performSelectorOnMainThread:@selector(stateChangeOnMainThread:) withObject: [NSDictionary dictionaryWithObjectsAndKeys:state, @"state", nil] waitUntilDone:YES];

Чтобы обнаружить места, где контекст вызывается из другого потока, вы можете установить точку останова в NSLog для функций, в которых вы вызываете контекст, как в следующем фрагменте кода, и просто использовать executeSelectorOnMainThread для них .

if(![NSThread isMainThread]){
     NSLog(@"Not the main thread...");
}

Я надеюсь, что это может быть полезно ...

6 голосов
/ 19 октября 2011

У меня была такая же проблема. Если вы работаете под отладчиком и когда приложение «зависает», остановите его (используйте кнопку «пауза» в отладчике. Если вы находитесь в строке executeFetchRequest, то проверьте переменную контекста. больше 1, затем он ожидает блокировки в соответствующем хранилище.

Где-то вы создаете условие гонки в связанном магазине.

4 голосов
/ 20 октября 2011

Это действительно похоже на попытку доступа к NSManagedObjectContext из потока / очереди, отличного от того, который его создал.Как и предполагали другие, вам нужно посмотреть на свои потоки и убедиться, что вы следуете правилам Core Data.

3 голосов
/ 23 августа 2012

Выполнение запроса на выборку должно происходить из потока, в котором был создан контекст.

Помните, что это не безопасно для потока, и попытка executeFetchRequest из другого потока приведет к непредсказуемому поведению.

В порядкечтобы сделать это правильно, используйте

[context performBlock: ^{
     NSArray * entities = [context executeFetchRequest:inFetchRequest error:&fetchError];   
}];

Это будет executeFetchRequest в том же потоке, что и контекст, который может быть или не быть основным потоком.

0 голосов
/ 01 апреля 2015

Удалить все объекты с 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
    }
0 голосов
/ 11 ноября 2013

В моем случае приложение зависнет перед «executeFetchRequest» без предупреждения. Решением было обернуть все операции с БД в @synchronized (persistentStore). Например:

NSArray *objects;
@synchronized([self persistentStoreCoordinator]) {
            objects = [moc executeFetchRequest:request error:&error];
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...