Помимо добавления свойства NSDate к каждому объекту в моем хранилище основных данных, существует ли программный способ получить дату модификации для любого объекта?
Получить дату модификации для NSManagedObject в Core Data?
Ответы (7)
Нет, вы должны добавить дату и управлять ею самостоятельно. Вы можете использовать переопределение -willSave
в своем управляемом объекте, чтобы обновить отметку времени, но прочитайте документацию API для NSManagedObject на -willSave
, чтобы узнать, как выполнить обновление, не вызывая цикла willSave (в документах даже говорится о случае обновления отметки времени). В документах также упоминается использование NSManagedObjectContextWillSaveNotification
, но это может вызвать больше проблем с настройкой, чем простая проверка, чтобы не устанавливать отметку времени слишком быстро.
Я лично проверяю, был ли изменен updatedAt
, и если да, то больше его не трогаю. Таким образом я прерываю цикл willSave
.
- (void)awakeFromInsert {
[super awakeFromInsert];
self.primitiveUpdatedAt = [NSDate date];
}
- (void)willSave {
[super willSave];
if(![self isDeleted] && self.changedValues[@"updatedAt"] == nil) {
self.updatedAt = [NSDate date];
}
}
Обратите внимание, что это решение предполагает, что у нас есть свойство с именем dateUpated в модели.
Вместо того, чтобы обрабатывать это на отдельных объектах. Я бы справился с этим через уведомление. Документация Apple также предлагает этот путь.
1. Зарегистрируйтесь для получения уведомления NSManagedObjectContextWillSaveNotification.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(willSaveContext:)
name:NSManagedObjectContextWillSaveNotification
object:nil];
2 Установите свойство для updatedDate в методе наблюдателя для каждого обновленного объекта.
- (void)willSaveContext:(NSNotification *)notification{
NSManagedObjectContext *context = [notification object];
NSSet *updatedObject = [context updatedObjects];
for (NSManagedObject *managedObject in [updatedObject allObjects]) {
if ([[managedObject.entity propertiesByName] objectForKey:@"dateUpdated"]) {
[managedObject setValue:[NSDate date] forKey:@"dateUpdated"];
}
}
}
На WWDC19 об этом было объявлено Производные атрибуты.
Атрибут updated
, подобный этому, автоматически обновляется Core Data:
Примечание. Это может привести к созданию недопустимой модели сопоставления.
Я нашел этот вопрос/ответ полезным для начала настройки атрибута даты, измененной в Core Data. В процессе я пришел к паре советов, которые также могут помочь:
(Совет 1: избегайте рекурсии willSave)
- Еще один способ избежать цикла willSave — написать специальную процедуру для сохранения контекста, которая выполняет итерацию по своим updateObjects в поисках тех, у которых есть свойство dateModified, и устанавливает его. Выполните фактические вызовы
commitEditing
иsave
после этого цикла. Не беспокойтесь о willSave вообще.
(Совет 2: живое обновление — но не работает с отменой)
Если вам нужно оперативное обновление для отображения даты, измененной для пользователя в таблице, вы также можете установить dateModified в вашем переключателе (или что-то еще) для столбца с измененной датой в вашем методе делегата objectValueForTableColumn (willDisplayCell для iOS).
Там проверьте, обновлен ли объект для этой строки, и если да, установите dateModified. Сомневаюсь, что
if (obj isUpdated)
чек стоит очень дорого. Но обязательно делайте это только для соответствующего столбца, чтобы не повторять без необходимости.Верните любое строковое представление, которое вы используете для столбца. Чтобы избежать очевидных несоответствий между временем фактического изменения и установленной датой, покажите только дату, а не время.
Это приведет к обновлению dateModified всякий раз, когда пользователь делает выбор таблицы, а также когда таблица перезагружается. Что не идеально: если пользователь изменяет атрибут, не представленный в таблице, столбец не будет обновляться, пока он не сделает выбор. Но это достаточно быстро и намного проще, чем реализация обширной схемы KVO.
(Вы все равно захотите установить дату в процедуре сохранения, чтобы уловить изменения, невидимые для таблицы.)
УСЛОЖНЕНИЕ ОТМЕНЫ: К сожалению, диспетчер отмены будет рассматривать изменение метода делегата таблицы на dateModifed как отдельное событие. Последующий вызов undo просто отменяет последнее изменение в dateModified — снова и снова. Я попытался обойти это, добавив средство отслеживания и проверку непустого словаря измененных значений, чтобы убедиться, что dateModified был установлен только после предварительного сохранения. Это работало для отмены удалений, но не для обычных правок. Таким образом, нет быстрого способа выполнить живое обновление предварительного сохранения dateModified.
Если кому-то нужен простой способ сделать это в Swift: я написал запись в блоге о простом классе UpdateListener
для обновления всех свойств updateDate
и insertDate
. Вы можете найти весь класс в этой сути. Все, что вам нужно сделать, это вызвать UpdateListener.setupSharedInstance()
в методе application(:didFinishLaunchingWithOptions:)
вашего делегата приложения.
Быстрая версия ответа Бена Аффлека
public override func willSave() {
super.willSave()
if !isDeleted, changedValues()["updatedAt"] == nil {
self.setValue(Date(), forKey: "updatedAt")
}
}