Core-Data将保存:方法

Mus*_*afa 21 core-data objective-c 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.' ***
Run Code Online (Sandbox Code Playgroud)

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

Pau*_*nge 47

事实上,苹果文档(在接受的答案中只读了一半)不推荐这种方法.他们明确表示你应该使用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
Run Code Online (Sandbox Code Playgroud)

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

  • 将类添加到ObjC运行时时调用load.这通常是在main.m文件中找到的全部封闭的@autoreleasepool之前.如果您没有自动释放池设置,对象将泄漏. (3认同)

Cal*_*ico 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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


zmi*_*mit 9

实际上,比接受的答案更好的方法是使用原始访问器,如NSManagedObject的文档中所建议的那样

`

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

`

此外,检查对象是否被标记为删除-isDeleted,因为-willSave这些也被调用.

  • 文档提到了有关覆盖willSave的问题:"如果使用原始访问器更改属性值,则可以避免无限递归的可能性,但Core Data不会注意到您所做的更改." (8认同)

Ric*_*ble 6

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

(在斯威夫特:)

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

    super.willSave()
}
Run Code Online (Sandbox Code Playgroud)

我需要这个的原因是因为我有一个特殊的要求,需要手动设置modifyDate.(我有时手动设置时间戳的原因是因为我试图使其与服务器上的时间戳保持同步.)

此解决方案:

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