Set a lastModificationDate attribute after NSManagedObjectsDidChangeNotification creates an infinite loop

試著忘記壹切 提交于 2019-12-22 11:25:21

问题


I added a lastModifiedDate attribute to all my entities to avoid duplicates when syncing the UIManagedDocument with iCloud, something that I found it can happen if I create new entities with an offline device (iPad) and, at the same time, I create the same entities using another online device (iPhone).

I wanted to set this attribute whenever an object changes so I subscribed for NSManagedObjectContextObjectsDidChangeNotification. The code I wrote to set the lastModifiedDate creates an infinite loop because by setting the lastModificationDate attribute it creates a change that will be notified again by NSManagedObjectContextObjectsDidChangeNotification and so on...

Is it possible to fix it? Is there a better way to accomplish my goal? Should I subclass managedObjectContext and override willSave:?

//At init...

[[NSNotificationCenter defaultCenter] addObserver:applicationDatabase
                                             selector:@selector(objectsDidChange:)
                                                 name:NSManagedObjectContextObjectsDidChangeNotification
                                               object:applicationDatabase.managedDocument.managedObjectContext];


(void) objectsDidChange: (NSNotification*) note
{
  // creates an infinite loop
    NSDate *dateOfTheLastModification = [NSDate date];
    NSMutableArray *userInfoKeys = [[note.userInfo allKeys] mutableCopy];

    for(int i=0; i< userInfoKeys.count;i++){
        NSString *key = [userInfoKeys objectAtIndex:i];
        if([key isEqualToString:@"managedObjectContext"]){
            [userInfoKeys removeObject:key];
        }
    }

    for(NSString *key in userInfoKeys){
        NSArray *detail = [note.userInfo objectForKey:key];
        for (id object in detail){

            [object setValue:dateOfTheLastModification forKey:@"lastModifiedDate"];
        }
}

回答1:


To avoid the infinite loop, you could set the last modification date using the primitive accessor:

[object setPrimitiveValue:dateOfTheLastModification forKey:@"lastModifiedDate"];

because that does not fire another "change" notification. But that also implies that no observers will see the change.

Overriding willSave in the managed object subclass would suffer from the same problem. The Apple documentation for willSave states:

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).

So you should register for NSManagedObjectContextWillSaveNotification instead, and set the timestamp on all updated and inserted objects in the managed object context. The registered method could look like this:

-(void)contextWillSave:(NSNotification *)notify
{
    NSManagedObjectContext *context = [notify object];
    NSDate *dateOfTheLastModification = [NSDate date];
    for (NSManagedObject *obj in [context insertedObjects]) {
        [obj setValue:dateOfTheLastModification forKey:@"lastModifiedDate"];
    }
    for (NSManagedObject *obj in [context updatedObjects]) {
        [obj setValue:dateOfTheLastModification forKey:@"lastModifiedDate"];
    }
}

This assumes that all your entities have a lastModifiedDate attribute, otherwise you have to check the class of the objects.



来源:https://stackoverflow.com/questions/20098544/set-a-lastmodificationdate-attribute-after-nsmanagedobjectsdidchangenotification

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!