I am slightly confused about parent/child contexts for ManagedObjectContext
.
When I setup a child context and set the parent context, does the child context contain all the objects of the parent context? I am using the stock Core Data
methods that get created in the AppDelegate
, but I changed the ConcurrencyQueue
to main.
In my method that is supposed to update the db:
- Create child context, set parent context
- Perform block on child context
- Fetch from parent context
- Create or update object in child context
- Call save on child context
- Have Notification listener to handle child context saves
- Save parent context
My issue is that it does not look I am saving anything to the child context. I am not getting my println messages of Update or Create ChatMessage. What am I doing wrong here?
AppDelegate Core Data methods
lazy var managedObjectContext: NSManagedObjectContext? = {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
if coordinator == nil {
return nil
}
var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
NSNotificationCenter.defaultCenter().addObserver(self, selector: "contextDidSave:", name: NSManagedObjectContextDidSaveNotification, object: nil)
return managedObjectContext
}()
func contextDidSave(notification: NSNotification) {
let sender = notification.object as! NSManagedObjectContext
if sender != managedObjectContext {
managedObjectContext?.mergeChangesFromContextDidSaveNotification(notification)
println("Core Data: merging changes from child context")
saveContext()
}
}
Database class that handles the update
lazy var parentContext: NSManagedObjectContext? = {
if let managedObjectContext = self.appDelegate.managedObjectContext {
return managedObjectContext
}
else {
return nil
}
}()
func updateMessage(chatMessage: ChatMessage) {
if chatMessage.id.isEmpty { return }
let childContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
childContext.parentContext = parentContext
childContext.performBlock({
let objectIdDesc = NSExpressionDescription()
objectIdDesc.name = "objectID"
objectIdDesc.expression = NSExpression.expressionForEvaluatedObject()
objectIdDesc.expressionResultType = NSAttributeType.ObjectIDAttributeType
let fetchRequest = NSFetchRequest(entityName: "ChatMessage")
fetchRequest.predicate = NSPredicate(format: "id == %@", chatMessage.id)
fetchRequest.propertiesToFetch = [objectIdDesc]
fetchRequest.resultType = .DictionaryResultType
var error: NSError?
if let results = self.parentContext!.executeFetchRequest(fetchRequest, error: &error) {
if error == nil {
if !results.isEmpty {
if let objectId = results[0].valueForKey("objectID") as? NSManagedObjectID {
let fetched = childContext.objectWithID(objectId) as! ChatMessage
fetched.id = chatMessage.id
fetched.senderUserId = chatMessage.senderUserId
fetched.senderUsername = chatMessage.senderUsername
fetched.receiverUserId = chatMessage.receiverUserId
fetched.receiverUsername = chatMessage.receiverUsername
fetched.messageType = chatMessage.messageType
fetched.message = chatMessage.message
fetched.timestamp = chatMessage.timestamp
fetched.filepath = chatMessage.filepath
println("Updated ChatMessage: \(fetched.id)")
}
else {
var newMessage = NSEntityDescription.insertNewObjectForEntityForName("ChatMessage", inManagedObjectContext: childContext) as! ChatMessage
newMessage.id = chatMessage.id
newMessage.senderUserId = chatMessage.senderUserId
newMessage.senderUsername = chatMessage.senderUsername
newMessage.receiverUserId = chatMessage.receiverUserId
newMessage.receiverUsername = chatMessage.receiverUsername
newMessage.messageType = chatMessage.messageType
newMessage.message = chatMessage.message
newMessage.timestamp = chatMessage.timestamp
newMessage.filepath = chatMessage.filepath
println("Create ChatMessage: \(newMessage.id)")
}
}
}
else {
println("Fetch Message Object ID Error: \(error?.localizedDescription)")
}
}
})
childContext.save(nil)
}
It does not seem to make too much sense to create a child context and then fetch from the parent context. I do not believe that this is the way child contexts' blocks were conceived to be used.
To clear up the confusion: after creating the child context from a parent context, that child context has the same "state" as the parent. Only if the two contexts do different things (create, modify, delete objects) the content of the two contexts will diverge.
So for your setup, proceed as follows:
- create the child context
- do the work you want to do (modifying or creating objects from the downloaded data),
- save the child context
At this stage, nothing is saved to the persistent store yet. With the child save, the changes are just "pushed up" to the parent context. You can now
- save the parent context
to write the new data to the persistent store. Then
- update your UI,
best via notifications (e.g. the NSManagedObjectContextDidSaveNotification
).
来源:https://stackoverflow.com/questions/30089401/core-data-should-i-be-fetching-objects-from-the-parent-context-or-does-the-chil