it-swarm.com.ru

Как я могу скопировать или переместить NSManagedObject из одного контекста в другой?

У меня есть то, что я предполагаю, является довольно стандартной установкой, с одним MOC блокнота, который никогда не сохраняется (содержащий кучу объектов, загруженных из Интернета), и другим постоянным MOC, который сохраняет объекты. Когда пользователь выбирает объект из scratchMOC для добавления в свою библиотеку, я хочу либо 1) удалить объект из scratchMOC и вставить его в перманентный MOC, либо 2) скопировать объект в перманентный MOC. Core Data FAQ говорит, что я могу скопировать объект следующим образом:

NSManagedObjectID *objectID = [managedObject objectID];
NSManagedObject *copy = [context2 objectWithID:objectID];

(В этом случае context2 будет перманентным MOC.) Однако, когда я делаю это, копируемый объект неисправен; данные изначально неразрешены. Когда это действительно разрешено, позже все значения равны нулю; никакие данные (атрибуты или отношения) из исходного управляемого объекта не копируются и не ссылаются на них. Поэтому я не вижу никакой разницы между использованием этого objectWithID: метода и просто вставкой совершенно нового объекта в constantMOC с использованием insertNewObjectForEntityForName :.

Я понимаю, что могу создать новый объект в перманентном МОК и вручную скопировать каждую пару ключ-значение из старого объекта, но я не очень доволен этим решением. (У меня есть несколько различных управляемых объектов, для которых у меня есть эта проблема, поэтому я не хочу писать и обновлять методы copy: для всех из них, поскольку я продолжаю разрабатывать.) Есть ли лучший способ?

51
Aeonaut

Во-первых, наличие более одного NSManagedObjectContext в одном потоке не является стандартной конфигурацией. В 99% случаев вам нужен только один контекст, и это решит эту ситуацию для вас.

Почему вы считаете, что вам нужно более одного NSManagedObjectContext?

Обновление

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

  1. Создать новый объект в постоянном контексте
  2. получить словарь атрибутов из исходного объекта (используйте -dictionaryWithValuesForKeys и -[NSEntityDescription attributesByName] для этого.
  3. установить словарь значений на целевой объект (используя -setValuesForKeysWithDictionary)
  4. Если у вас есть отношения, вам нужно будет делать эту копию рекурсивно и обходить отношения либо жестко закодировано (чтобы избежать некоторой циклической логики), либо с помощью -[NSEntityDescription relationshipsByName]

Как уже упоминалось, вы можете загрузить пример кода из моей книги из Базовая книга данных программистов Pragmatic и увидеть одно решение этой проблемы. Конечно, в книге я обсуждаю это более подробно :)

52
Marcus S. Zarra

Документация вводит в заблуждение и неполна. Методы objectID сами по себе не копируют объекты, они просто гарантируют, что вы получили нужный объект.

Фактически context2 в этом примере является исходным контекстом, а не местом назначения. Вы получаете ноль, потому что целевой контекст не имеет объекта с этим идентификатором.

Копирование управляемых объектов довольно сложное из-за сложности графа объектов и способа управления графом контекстом. Вы должны воссоздать скопированный объект подробно в новом контексте.

Вот пример кода , который я вырвал из кода примера для Основные данные прагматического программиста : API Apple для хранения данных в Mac OS X . (Возможно, вы сможете загрузить весь код проекта, не покупая книгу на сайте Pragmatic.) Она должна дать вам приблизительное представление о том, как выполнить копирование объекта между контекстами.

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

9
TechZen

У меня была такая же проблема, и я нашел эту статью о созданных отключенных объектах, которые впоследствии можно было добавить в контекст: http://locassa.com/teilitary-storage-in-apples-coredata/

Идея в том, что у вас есть NSManagedObject, потому что вы собираетесь хранить объекты в базе данных. Мое препятствие состояло в том, что многие из этих объектов загружаются через HTTP API, и я хочу выбросить большинство из них в конце сеанса. Подумайте о потоке пользовательских сообщений, и я хочу сохранить только те, которые были добавлены в избранное или сохранены как черновик.

Я создаю все свои сообщения, используя

+ (id)newPost {
    NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Post" inManagedObjectContext:self.managedObjectContext];
    Post *post = [[Post alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:nil];
    return post;
}

а затем сообщения вставляются в локальный контекст управляемого объекта, когда они предпочтительны

+ (BOOL)favoritePost:(Post *)post isFavorite:(BOOL)isFavorite
{
    // Set the post's isFavorite flag
    post.isFavorite = [NSNumber numberWithBool:isFavorite];

    // If the post is being favorited and not yet in the local database, add it
    NSError *error;
    if (isFavorite && [self.managedObjectContext existingObjectWithID:post.objectID error:&error] == nil) {
        [self.managedObjectContext insertObject:post];
    }
    // Else if the post is being un-favorited and is in the local database, delete it
    else if (!isFavorite && [self.managedObjectContext existingObjectWithID:post.objectID error:&error] != nil) {
        [self.managedObjectContext deleteObject:post];
    }

    // If there was an error, output and return NO to indicate a failure
    if (error) {
        NSLog(@"error: %@", error);
        return NO;
    }

    return YES;
}

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

6
Anthony

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

Согласно документация , objectWithID: всегда возвращает объект. Таким образом, тот факт, что ошибка разрешается для объекта, будет иметь все значения nil, что означает, что он не находит ваш объект в постоянном хранилище.

1
Alex