问题
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