确定保存 NSManagedObjectContext 是否会导致对持久存储的更改
Determine if saving NSManagedObjectContext will result in changes to persistent store
换句话说,是否可以通过丢弃当前上下文来确定是否有更改会丢失?现在我的 UI 表示如果 context.hasChanges == TRUE
有数据丢失的风险,但我认为 hasChanges
看起来像这样:
- (BOOL) hasChanges {
return self.updatedObjects > 0 || self.insertedObjects.count > 0 || self.deletedObjects.count > 0;
}
...和 updatedObjects
包含仅 编辑过 的对象,即使属性未更改其原始值也是如此。 updatedObjects
还包含具有瞬态变化的对象。保存这些对象不会修改持久存储。
如果我的用户实际上没有更改任何内容,我不想提示他们进行保存,那么确定持久属性是否发生更改的最佳方法是什么?
也许您可以使用 committedValuesForKeys:
获取假定更改的键的旧值并与新值进行比较。像这样:
@interface NSManagedObject (tofustew)
- (BOOL)isActuallyUpdated;
@end
@implementation NSManagedObject (tofustew)
- (BOOL)isActuallyUpdated {
NSArray *keys = self.changedValues.keyEnumerator.allObjects;
NSDictionary *newValues = [self dictionaryWithValuesForKeys:keys];
NSDictionary *oldValues = [self committedValuesForKeys:self.changedValues.keyEnumerator.allObjects];
return [oldValues isEqualToDictionary:newValues];
}
@end
@interface NSManagedObjectContext (tofustew)
- (BOOL)hasActualChanges;
@end
@implementation NSManagedObjectContext (tofustew)
- (BOOL)hasActualChanges {
if (self.insertedObjects.count > 0 || self.deletedObjects.count > 0)
return YES;
for (NSManagedObject *object in self.updatedObjects) {
if (object.isActuallyUpdated) {
return YES;
}
}
return NO;
}
@end
这个 SO post 解决了一个类似的问题:
Identifying which fields have changed before CoreData saves
This method only reports changes to properties that are defined as
persistent properties of the receiver, not changes to transient
properties or custom instance variables. This method does not
unnecessarily fire relationship faults.
就像过滤 updatedObjects
一样简单:
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"changedValues[SIZE] > 0"];
NSSet *objectsWithChangedValues = [context.updatedObjects filteredSetUsingPredicate:predicate];
几行代码产生了预期的结果,但我仍然不明白为什么 updatedObjects
会这样工作...
由于您只关心是否存在更改,而不是确切地找出发生了什么更改,因此我将使用 NSManagedObject
上的 hasPersistentChangedValues
方法来过滤上下文的字段。例如,
NSPredicate *hasChanges = [NSPredicate predicateWithFormat:@"hasPersistentChangedValues = YES"];
BOOL changesExist = ([[context.updatedObjects filteredSetUsingPredicate:hasChanges] count] > 0);
相同的逻辑适用于插入和删除的对象。
我最终在 Swift 中编写了以下方法:
/// Checks whether there are actually changes that will change the persistent store.
/// The `hasChanges` method would return `true` for transient changes as well which can lead to false positives.
var hasPersistentChanges: Bool {
return !insertedObjects.isEmpty || !deletedObjects.isEmpty || updatedObjects.first(where: { [=10=].hasPersistentChangedValues }) != nil
}
结合以下 saveIfNeeded 方法,这将导致只需要保存:
/// Saves the context, only if it has any changes and if it has a place to save the changes to (parent or persistent store). Calling this method instead of the regular save() will increase performance. It is also useful to use this function when having a memory store, since this configuration doesn't have a persistent store but may use parent contexts to save their changes to.
/// - throws: A Core Data NSError when saving fails.
/// - returns: Whether the save was needed.
@discardableResult public func saveIfNeeded() throws -> Bool {
let hasPurpose = parent != nil || persistentStoreCoordinator?.persistentStores.isEmpty == false
guard hasPersistentChanges && hasPurpose else {
// Saving won't do anything now, except for decreasing performance. Skip it for now.
return false
}
try save()
return true
}
换句话说,是否可以通过丢弃当前上下文来确定是否有更改会丢失?现在我的 UI 表示如果 context.hasChanges == TRUE
有数据丢失的风险,但我认为 hasChanges
看起来像这样:
- (BOOL) hasChanges {
return self.updatedObjects > 0 || self.insertedObjects.count > 0 || self.deletedObjects.count > 0;
}
...和 updatedObjects
包含仅 编辑过 的对象,即使属性未更改其原始值也是如此。 updatedObjects
还包含具有瞬态变化的对象。保存这些对象不会修改持久存储。
如果我的用户实际上没有更改任何内容,我不想提示他们进行保存,那么确定持久属性是否发生更改的最佳方法是什么?
也许您可以使用 committedValuesForKeys:
获取假定更改的键的旧值并与新值进行比较。像这样:
@interface NSManagedObject (tofustew)
- (BOOL)isActuallyUpdated;
@end
@implementation NSManagedObject (tofustew)
- (BOOL)isActuallyUpdated {
NSArray *keys = self.changedValues.keyEnumerator.allObjects;
NSDictionary *newValues = [self dictionaryWithValuesForKeys:keys];
NSDictionary *oldValues = [self committedValuesForKeys:self.changedValues.keyEnumerator.allObjects];
return [oldValues isEqualToDictionary:newValues];
}
@end
@interface NSManagedObjectContext (tofustew)
- (BOOL)hasActualChanges;
@end
@implementation NSManagedObjectContext (tofustew)
- (BOOL)hasActualChanges {
if (self.insertedObjects.count > 0 || self.deletedObjects.count > 0)
return YES;
for (NSManagedObject *object in self.updatedObjects) {
if (object.isActuallyUpdated) {
return YES;
}
}
return NO;
}
@end
这个 SO post 解决了一个类似的问题:
Identifying which fields have changed before CoreData saves
This method only reports changes to properties that are defined as persistent properties of the receiver, not changes to transient properties or custom instance variables. This method does not unnecessarily fire relationship faults.
就像过滤 updatedObjects
一样简单:
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"changedValues[SIZE] > 0"];
NSSet *objectsWithChangedValues = [context.updatedObjects filteredSetUsingPredicate:predicate];
几行代码产生了预期的结果,但我仍然不明白为什么 updatedObjects
会这样工作...
由于您只关心是否存在更改,而不是确切地找出发生了什么更改,因此我将使用 NSManagedObject
上的 hasPersistentChangedValues
方法来过滤上下文的字段。例如,
NSPredicate *hasChanges = [NSPredicate predicateWithFormat:@"hasPersistentChangedValues = YES"];
BOOL changesExist = ([[context.updatedObjects filteredSetUsingPredicate:hasChanges] count] > 0);
相同的逻辑适用于插入和删除的对象。
我最终在 Swift 中编写了以下方法:
/// Checks whether there are actually changes that will change the persistent store.
/// The `hasChanges` method would return `true` for transient changes as well which can lead to false positives.
var hasPersistentChanges: Bool {
return !insertedObjects.isEmpty || !deletedObjects.isEmpty || updatedObjects.first(where: { [=10=].hasPersistentChangedValues }) != nil
}
结合以下 saveIfNeeded 方法,这将导致只需要保存:
/// Saves the context, only if it has any changes and if it has a place to save the changes to (parent or persistent store). Calling this method instead of the regular save() will increase performance. It is also useful to use this function when having a memory store, since this configuration doesn't have a persistent store but may use parent contexts to save their changes to.
/// - throws: A Core Data NSError when saving fails.
/// - returns: Whether the save was needed.
@discardableResult public func saveIfNeeded() throws -> Bool {
let hasPurpose = parent != nil || persistentStoreCoordinator?.persistentStores.isEmpty == false
guard hasPersistentChanges && hasPurpose else {
// Saving won't do anything now, except for decreasing performance. Skip it for now.
return false
}
try save()
return true
}