iOS Core Data, когда сохранять контекст? - PullRequest
19 голосов
/ 10 декабря 2011

У меня случайные сбои с основными данными из-за параллелизма и многопоточности. Я знаю, что данные ядра не являются потокобезопасными. Я также нашел пару других ответов о том, как создать ThreadedDataService и создать отдельный контекст для каждого потока.

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

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

Читая документацию App Delegate, я заметил, что она советует мне сохранять контекст в applicationWillTerminate.

У меня такой вопрос: Для длительной операции, которая вставляет новые события каждую минуту, и пользователю не требуется видеть обновления, распространяемые сразу на все контроллеры, когда мне пора сохранить контекст У меня такое ощущение, что сохранение контекста для каждой записи может быть излишним?

-(void)insertNewEvent
{


    // Create a new instance of the entity managed by the fetched results controller.
    NSManagedObjectContext *context = [self.fetchedResultsController.managedObjectContext];
    NSEntityDescription *entity = [[self.fetchedResultsControllerfetchRequest] entity];
    Event*newManagedObject = (Event*)[NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];

//use accessor methods to set default values

    // Save the context. > IS THIS REALLY NEEDED HERE?
    NSError *error = nil;
    if (![context save:&error])
    {


    }else
    {
        if(newManagedObject!=nil)
        {
            currentState= newManagedObject;
            [currentRecord addEvent:newManagedObject];
//Is this call needed?
            [self saveApplicationRecords];      
        }
    }

}

У меня есть методы, подобные этим, определенные для всех моих управляемых объектов, достаточно ли этого, если я вызываю такой метод в главном потоке каждые 10-15 минут, чтобы сохранять ожидающие изменения, а не делать это после каждой вставки записи?

-(void)saveApplicationRecords
{
     NSLog(@"saveApplicationRecords");
    NSManagedObjectContext *context = [self.applicationRecordsController.managedObjectContext];
    // Save the context.
    NSError *error = nil;
    if (![context save:&error])
    {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);

    }

}

Дополнительный вопрос после прочтения ответа macbirdie: допустим ли этот метод в основных данных?

-(Event*)insertAndReturnNewEventWithDate:(NSDate*)date_ type:(int)type
{
 NSManagedObjectContext *context = [self.dreamEventsController managedObjectContext];
    NSEntityDescription *entity = [[self.dreamEventsController fetchRequest] entity];
    DreamEvent *newManagedObject = (Event*)[NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];

//handle properties

 NSError *error = nil;
    if (![context save:&error])
    {
return nil;

    }else
    {
return newManagedObject ;
}

}

Спасибо!

1 Ответ

17 голосов
/ 10 декабря 2011

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

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

По умолчанию объект, созданный и возвращенный NSManagedObjectContext, автоматически освобождается. Если вы создали новый объект и хотите отредактировать его, например, на листе формы, вы можете вызвать setRetainsRegisteredObjects: с YES в контексте управляемого объекта перед созданием объекта, поэтому он будет удерживаться на созданном объекте до сделано с этим. Помните, что не рекомендуется самостоятельно управлять жизненным циклом NSManagedObject - вы должны позволить NSManagedObjectContext сделать это. Итак, имея это в виду, вам не нужно сохранять NSManagedObject. После операции сохранения он не регистрируется контекстом и удаляется из памяти. С вашей стороны ничего не требуется.

Ответ на обновленную часть вопроса

Вероятно, было бы лучше, если бы вы вернули NSManagedObjectID (используя [object objectID]) вместо самого объекта. Он позволяет безопасно распоряжаться объектом по контексту, и, если он нужен для дальнейшего редактирования или извлечения данных (например, из других контекстов), они могут извлечь его из хранилища отдельно.

Даже если вы не сохраните контекст, вновь созданный объект будет тут же, и вы сможете решить, хотите ли вы сохранить объект или нет.

С другой стороны, после сохранения, если контекст все еще существует, он может вернуть вам объект с указанным NSManagedObjectID из кэша памяти - не касаясь базы данных.

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

...