Отмена ввода данных Core Data, которые выполняются из основного потока

Я работаю над кодом, который использует NSOperation для импорта данных. Я хотел бы, чтобы пользователь мог отменить экземпляры NSManagedObject, созданные во время операции импорта.

Из того, что я могу сказать, невозможно использовать NSManagedObjectContext -undoManager для любых операций, выполняемых с основного потока. Из раздела "Руководство по программированию основных данных" в разделе "Использование ограничения потока для поддержки Concurrency" мы имеем следующие два условия:

  • Необходимо передать только идентификатор объекта между контекстами управляемого объекта (on отдельные потоки)
  • Управляемые объекты должны быть сохранены в контексте до можно использовать идентификатор объекта.

Это имеет смысл, поскольку управляемые объекты необходимо перенести из частного хранилища (NSManagedObjectContext) в общедоступное хранилище (NSPersistentStore), прежде чем их можно будет использовать.

К сожалению, сообщение -save: также вызывает удаление любых управляемых объектов в стеке отмены. Из раздела "Управление памятью с использованием основных данных" одного и того же руководства:

Управляемые объекты, которые находятся на рассмотрении изменения (вставки, удаления или обновления) сохраняются по их контексту пока их контекст не будет отправлен save:, reset, откат или сообщение dealloc, или соответствующее количество отменить отмените изменение.

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

-

Был представлен расширенный радар: rdar://проблема/8977725

Ответы

Ответ 1

Этот ответ, вероятно, будет немного назад и вперед. Если я правильно понял проблему, вы делаете импорт, но когда импорт сделан, вы хотите, чтобы пользователь мог выбрать, что будет сохранено из импорта?

Если это неверно, исправьте мои предположения, и я обновлю этот ответ.

Если это правильно, то что вы можете сделать:

  • Измените создание фонового объекта на

    NSEntityDescription *myEntity = ... //Entity from your context
    [[NSManagedObject alloc] initWithEntity:myEntity
             insertIntoManagedObjectContext:nil];
    
  • Храните эти объекты в массиве.
  • При необходимости передайте объекты обратно в основной поток.
  • Отпустите все объекты, которые вы не хотите сохранять
  • Вызовите [myMainContext insertObject:managedObject] на любом, что вы хотите сохранить.
  • Выполните сохранение на NSManagedObjectContext.

Поскольку эти объекты не являются частью NSManagedObjectContext, но они существуют только в памяти и должны быть потокобезопасными, поскольку они еще не привязаны к NSManagedObjectContext.

Это, конечно, теоретически и потребует тестирования. Однако он должен выполнить вашу задачу.

Ответ 2

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

Ответ 3

Предположим, что вы используете отдельный контекст для фонового потока, и как только это делается, нажимайте [[backgroundContext undoManager] undo] на стеке отмены нити переднего плана? Я никогда не пробовал ничего подобного, но с головы до головы я не могу думать о причине, по которой он не должен работать.

Ответ 4

Один из вариантов может заключаться в том, чтобы сделать поток импорта постоянным. Даже когда поток завершен, он переходит в состояние бездействия. Таким образом, ваш threaded ManagedObjectContext сохраняется в соответствующем потоке. Затем, когда пользователь хочет отменить изменение, отправьте сообщение в поток, чтобы использовать нестационар.

Ответ 5

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

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

Неэлегантный, но работоспособный.