Объединение отмены Core Data для действия, охватывающего несколько циклов выполнения

У меня есть приложение UIKit на основе Core Data, которое позволяет пользователю перетаскивать объекты на экране. Пока объект перетаскивается, я обновляю его атрибут положения при каждом событии touchesMoved:. Чтобы поддержать отмену операции перетаскивания за один раз, я создаю новую группу отмены в начале перетаскивания и закрываю группу, когда пользователь поднимает палец.

Чтобы сэкономить память и сделать операции отмены быстрыми, я хочу объединить данные отмены, относящиеся к операции перетаскивания, но Core Data делает это сложным. Проблема в том, что processPendingChanges вызывается в конце каждого цикла цикла выполнения и заставляет Core Data сохранять новую запись отмены для изменения позиции, которое произошло в этой итерации. Операция перетаскивания может легко накопить сотни таких записей отмены, все из которых, кроме первой, не нужны.

Есть ли способ продолжать использовать волшебную встроенную поддержку отмены в Core Data, но не тратить драгоценную память на такие повторяющиеся записи отмены? Мне нравится, что мне не нужно заботиться о поддержании согласованности графа объектов при операциях отмены/возврата, но неспособность правильно обрабатывать эти непрерывные обновления атрибутов, похоже, мешает.


person Karoy Lorentey    schedule 21.07.2010    source источник


Ответы (2)


Я думаю, что установка менеджеров отмены setGroupsByEvent: сделает то, что вы хотите.

Задает логическое значение, указывающее, будет ли приемник автоматически группировать операции отмены во время цикла выполнения. Если YES, получатель создает группы отмены вокруг каждого прохода через цикл выполнения; если НЕТ, то нет.

Более чистое решение может состоять в том, чтобы просто не фиксировать положение объектов в модели данных до конца события перетаскивания.

person TechZen    schedule 22.07.2010
comment
К сожалению, setGroupsByEvent: здесь не поможет. Перетаскивание охватывает несколько событий, поэтому, если я не понял что-то основное, нет способа явно сгруппировать мои изменения с помощью beginUndoGrouping/endUndoGrouping — и setGroupsByEvent становится неактивным, когда уже есть открытая группа. Не менять модель до конца жеста перетаскивания, вероятно, лучший способ. Проблема в том, что мне негде хранить транзиентную позицию — я делаю рендер прямо из модели, без отдельных вью-объектов. Но, возможно, это просто означает, что пришло время добавить их прямо сейчас! - person Karoy Lorentey; 24.07.2010
comment
IIRC, установив groupsByEvent на NO, вы получите одну длинную группу отмены, которая выходит за пределы цикла выполнения. - person TechZen; 24.07.2010
comment
Вздох Нет, установка groupsByEvent на NO просто отключает неявную группировку, поэтому вам нужно всегда начинать и заканчивать свои собственные группы. Да, группы, которые вы сами создаете, очевидно, могут превзойти одну итерацию цикла выполнения. Вопрос был не в этом. Вопрос заключался в использовании памяти и проблеме с производительностью, возникающей из-за автоматического вызова performPendingChanges в конце каждой итерации цикла выполнения. Предложенное вами более чистое решение оказывается хорошим обходным путем: если модель изменяется только в конце жеста, performPendingChanges не будет регистрировать новое действие отмены в каждой итерации цикла выполнения. - person Karoy Lorentey; 25.07.2010
comment
Хорошо, я давно не возился с этим. - person TechZen; 25.07.2010

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

Если у вас включен groupsByEvent, вам нужно помнить, что менеджер отмены игнорирует все сообщения о группировке, когда регистрация отключена, включая сообщение, которое автоматически завершает неявную группу в конце события. Поэтому, если вы планируете оставить регистрацию отключенной в конце цикла выполнения, вам придется вручную закрыть неявную группу самостоятельно:

[moc processPendingChanges];
while ([moc.undoManager groupingLevel])
    [moc.undoManager endUndoGrouping];
[moc.undoManager disableUndoRegistration];

После завершения жеста перетаскивания вы можете снова включить отмену регистрации с помощью следующего кода:

[moc processPendingChanges];
[moc.undoManager enableUndoRegistration];

Это решение работает, но оно немного неуклюже. Вариант, предложенный TechZen, намного чище: не обновляйте атрибуты модели до тех пор, пока не будет выполнен жест перетаскивания.

person Karoy Lorentey    schedule 23.07.2010
comment
Оказалось, что более чистый подход плохо подходил для моего приложения — систематическое введение отдельного (независимо настраиваемого) дерева представлений параллельно с объектами моей модели привело бы к слишком большим сложностям. В итоге я использовал это более простое решение. - person Karoy Lorentey; 26.07.2010