Core-Data将保存:方法

时间:2011-02-02 11:58:37

标签: objective-c core-data nsmanagedobject

我的modificationDate中有一个属性Entity A.我想在保存NSManagedObject时设置其值。但是,如果我尝试在NSManagedObject willSave:方法中执行此操作,则会出现错误:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Failed to process pending changes before save.  The context is still dirty after 100 attempts.  Typically this recursive dirtying is caused by a bad validation method, -willSave, or notification handler.' ***

所以,我想知道,设置modificationDate的价值的最佳方式是什么?

5 个答案:

答案 0 :(得分:47)

实际上apple docs(在接受的答案中只读了一半)不推荐这种方法。他们明确表示你应该使用NSManagedObjectContextWillSaveNotification。一个例子可能是:

@interface TrackedEntity : NSManagedObject
@property (nonatomic, retain) NSDate* lastModified;
@end

@implementation TrackedEntity
@dynamic lastModified;

+ (void) load {
    @autoreleasepool {
       [[NSNotificationCenter defaultCenter] addObserver: (id)[self class]
                                                selector: @selector(objectContextWillSave:)
                                                    name: NSManagedObjectContextWillSaveNotification
                                                  object: nil];
    }
}

+ (void) objectContextWillSave: (NSNotification*) notification {
   NSManagedObjectContext* context = [notification object];
   NSSet* allModified = [context.insertedObjects setByAddingObjectsFromSet: context.updatedObjects];
   NSPredicate* predicate = [NSPredicate predicateWithFormat: @"self isKindOfClass: %@", [self class]];
   NSSet* modifiable = [allModified filteredSetUsingPredicate: predicate];
   [modifiable makeObjectsPerformSelector: @selector(setLastModified:) withObject: [NSDate date]];
}
@end

我使用它(使用其他一些方法:例如主键)作为大多数核心数据项目的抽象基类。

答案 1 :(得分:32)

来自willSave的NSManagedObject文档:

  

如果要更新持久属性值,通常应在进行更改之前测试任何新值与现有值的相等性。如果使用标准访问器方法更改属性值,Core Data将观察生成的更改通知,因此在保存对象的托管对象上下文之前再次调用willSave。如果继续修改willSave中的值,willSave将继续被调用,直到程序崩溃。

     

例如,如果设置了上次修改的时间戳,则应检查先前是否在同一个保存操作中设置了它,或者现有时间戳是否不小于当前时间的小增量。通常,最好为所有保存的对象计算一次时间戳(例如,响应NSManagedObjectContextWillSaveNotification)。

所以可能有以下几点:

-(void)willSave {
    NSDate *now = [NSDate date];
    if (self.modificationDate == nil || [now timeIntervalSinceDate:self.modificationDate] > 1.0) {
        self.modificationDate = now;
    }
}

您可以调整1.0以反映预期保存请求之间的最小增量。

答案 2 :(得分:9)

实际上比接受的答案更好的方法是使用原始访问器,如NSManagedObject's Documentation中所述

`

- (void)willSave
{
    if (![self isDeleted])
    {
        [self setPrimitiveValue:[NSDate date] forKey:@"updatedAt"];
    }
    [super willSave];
}

`

另外,检查对象是否标记为-isDeleted删除,因为-willSave也会被调用。

答案 3 :(得分:6)

这个问题显然有几个很好的解决方案,但是我想抛出一个最适合我遇到的特定场景的新解决方案。

(在斯威夫特:)

override func willSave() {
    if self.changedValues()["modificationDate"] == nil {
        self.modificationDate = NSDate()
    }

    super.willSave()
}

我需要这个的原因是因为我有一个特殊的需要有时需要手动设置modificationDate 。 (我有时手动设置时间戳的原因是因为我试图让它与服务器上的时间戳保持同步。)

此解决方案:

  1. 防止无限的willSave()循环,因为一旦设置了时间戳,它将出现在changedValues()
  2. 不需要使用观察
  3. 允许手动设置时间戳

答案 4 :(得分:0)

Swift 4解决方案,它是zmit和Richard答案的组合,无需重复NSNotification

override func willSave() {
    let expectedNewValue = "Your new value"
    if customField != expectedNewValue, changedValues()[#keyPath(Entity.customField)] == nil, !isDeleted {
        customField = expectedNewValue
    }
    super.willSave()
}