简体   繁体   English

Core-Data将保存:方法

[英]Core-Data willSave: method

I have an attribute modificationDate in my Entity A. I want to set its value whenever NSManagedObject is saved. 我的Entity A.有一个属性modificationDate Entity A.我想在保存NSManagedObject时设置其值。 However, if i try to do that in NSManagedObject willSave: method, i get an error: 但是,如果我尝试在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.' ***

So, i'm wondering, what's the best way to set the value of modificationDate ? 所以,我想知道,设置modificationDate值的最佳方法是什么?

In fact the apple docs (which are only half read in the accepted answer) don't recommend this method. 事实上, 苹果文档 (在接受的答案中只读了一半)不推荐这种方法。 They explicitly say you should use NSManagedObjectContextWillSaveNotification. 他们明确表示你应该使用NSManagedObjectContextWillSaveNotification。 An example might be: 一个例子可能是:

@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

I use this (with a few other methods: primary key for example) as an abstract base class for most core data projects. 我使用它(使用其他一些方法:例如主键)作为大多数核心数据项目的抽象基类。

From the NSManagedObject docs for willSave: 来自willSave的NSManagedObject文档:

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. 如果使用标准访问器方法更改属性值,Core Data将观察生成的更改通知,因此在保存对象的托管对象上下文之前再次调用willSave。 If you continue to modify a value in willSave, willSave will continue to be called until your program crashes. 如果继续修改willSave中的值,willSave将继续被调用,直到程序崩溃。

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). 通常,最好为所有保存的对象计算一次时间戳(例如,响应NSManagedObjectContextWillSaveNotification)。

So maybe something along the lines of: 所以可能有以下几点:

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

Where you can adjust the 1.0 to reflect the minimum delta between your expected save requests. 您可以在哪里调整1.0以反映预期保存请求之间的最小增量。

Actually a much better way than the accepted answer would be to use primitive accessors, as suggested in NSManagedObject's Documentation 实际上,比接受的答案更好的方法是使用原始访问器,如NSManagedObject的文档中所建议的那样

` `

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

` `

Also, check whether the object is marked for deletion with -isDeleted , as -willSave gets called for those too. 另外,检查对象是否使用-isDeleted标记为删除,因为-willSave被调用。

There are obviously several good solutions to this question already, but I wanted to throw out a new one that worked best for one particular scenario I encountered. 显然这个问题有几个很好的解决方案,但我想抛出一个最适合我遇到的特定场景的新解决方案。

(In Swift:) (在斯威夫特:)

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

    super.willSave()
}

The reason I needed this is because I have the peculiar requirement of needing to sometimes set the modificationDate manually . 我需要这个的原因是因为我有一个特殊的要求,需要手动设置modifyDate (The reason I sometimes set the time stamp manually is because I try to keep it in sync with a time stamp on the server.) (我有时手动设置时间戳的原因是因为我试图使其与服务器上的时间戳保持同步。)

This solution: 此解决方案:

  1. Prevents the infinite willSave() loop because once the time stamp is set, it will appear in changedValues() 防止无限的willSave()循环,因为一旦设置了时间戳,它将出现在changedValues()中
  2. Doesn't require using observation 不需要使用观察
  3. Allows for setting the time stamp manually 允许手动设置时间戳

Swift 4 solution which is a combination of zmit and Richard answer without the need of recurring to NSNotification : 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()
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM