2011-02-02 113 views
21

我有一个属性modificationDate在我的Entity A.我想设置它的值,只要NSManagedObject保存。但是,如果我尝试这样做,在NSManagedObjectwillSave:方法,我得到一个错误:核心数据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价值的最佳方式是什么?

回答

32

从NSManagedObject文档的willSave:

If you want to update a persistent property value, you should typically test for equality of any new value with the existing value before making a change. If you change property values using standard accessor methods, Core Data will observe the resultant change notification and so invoke willSave again before saving the object’s managed object context. If you continue to modify a value in willSave, willSave will continue to be called until your program crashes.

For example, if you set a last-modified timestamp, you should check whether either you previously set it in the same save operation, or that the existing timestamp is not less than a small delta from the current time. Typically it’s better to calculate the timestamp once for all the objects being saved (for example, in response to an NSManagedObjectContextWillSaveNotification).

所以也许东西沿着线:

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

在那里你可以调整1.0,以反映预期的保存请求之间的最小增量。

+0

你还需要检查,如果时间modificationdate在昌edValues – malhal 2015-09-02 12:16:15

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 

我用这个(少数其他方法:例如主键)作为用于最核心的数据项目的抽象基类。

+0

我喜欢!只是寻找这样的东西 - 欣赏示例代码。我是否需要致电[加载超级]?(如果NSManagedObject或它的父节点不执行任何操作,则怀疑否。) – 2012-05-23 18:12:14

+1

OK!我尝试了这一点,似乎太过分了(或者更可能是我滥用了它)。我有一个使用lastModified的管理对象(通过MYAPPTrackedManagedObject,基本上是TrackedEntity)。但是,此对象包含与其他不使用lastModified的对象的关系。因此,setLastModified:将不会被其他对象识别,并抛出异常。也许我需要以某种方式拨回它? – 2012-05-23 19:49:57

+0

啊,也许只是走allModified并过滤掉任何不响应setLastModified的东西,然后使用它。 – 2012-05-23 19:56:20

9

其实一个更好的方法比接受的答案是使用原始的存取,如NSManagedObject's Documentation

`

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

`

还有人建议,检查对象是否被标记为删除-isDeleted,因为-willSave也被要求。

6

这个问题已经有很多很好的解决方案了,但我想抛出一个最适合我遇到的特定场景的新解决方案。

(在斯威夫特:)

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

    super.willSave() 
} 

,我需要这样做的原因是因为我需要有时手动设定的时间modificationdate的特殊要求。 (我有时手动设置时间戳记的原因是因为我尝试与服务器上的时间戳保持同步。)

该解决方案:

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