2011-02-02 114 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. 允許設置時間戳手動