I have three contexts:
masterMOC - private queue tied to the persistent store, so physical saves happen here
----mainMOC - main queue tied to the UI, child of masterMOC
-------backgroundMOC - private queue, child of mainMOC
Let's see I create an Employee object on the mainMOC, then save the mainMOC. Then I save the masterMOC (which writes to disk).
Now, I've saved the Employee NSManagedObjectID in a variable, objectID. I want to get this Employee on the backgroundMOC. Does [backgroundMOC objectWithId:objectID] serve this purpose? Will it go to the persistent store and fetch this object using that method? Or will I have to preform a fetch request?
Your'e doing it right. They want you to pass objects between MOContexts using ID's. objectWithId will hit the persistent store and load the object in a fresh state.
The only gotcha you have to worry about is this case.
You fetch an object or create a new object in a MOContext.
You try pass the objectID to another context WITHOUT SAVING
The new MOContext wont know about the updates, and if you created a new object the objectID wouldn't be in the persistent store, so I think it returns nil or it's not defined.
There is a WWDC video from this year titled 'Core Data Best Practices' that talks about nested MOC's. But to answer your question, yes, objectWithId will travel up through the fewest number of MOC levels to find the object. So if you call [backgroundMOC objectWithId:objectID] and the object exists in the mainMOC, it will get it from the mainMOC without having to go all the way to the masterMOC or the database.
Related
I want to have table in CoreData that holds a list of other CoreData objects I have accessed, for instance I have Clients and I want a table RecentClients that is simply holding this list and the date they were accessed.
Can I store the objectID and then do a fetch request based on that?
EDIT:
See Ben's answer below and then go here:
http://cocoawithlove.com/2008/08/safely-fetching-nsmanagedobject-by-uri.html
You'll want to convert the NSManagedObjectID to a string by calling its -URIRepresentation method. You can then convert the string back to an NSManagedObjectID using NSPersistentStore's -managedObjectIDForURIRepresentation: method.
If you store the strings, you should be able to do what you're describing, though you won't use a fetch request; you'll use -[NSManagedObjectContext objectWithID:]
How are you planning on storing the access date? If you make it an attribute of your Client entity, you can bump it each time the object is accessed and then use an NSFetchedResultsController that fetches Clients ordered by the access date. The downside is, of course, that you're modifying the instance every time you access it, which may not be ideal.
You could just create the RecentClients as another entity in your Core Data model. It would then have a one to many relationship with the Client entity.
I am trying to figure out the best way to bulk delete objects inside of my Core Data database.
I have some objects with a parent/child relationship. At times I need to "refresh" the parent object by clearing out all of the existing children objects and adding new ones to Core Data. The 'delete all' portion of this operation is where I am running into trouble.
I accomplish this by looping through the children and calling deleteObject for each one.
I have noticed that after the NSManagedObjectContext:Save call following all of the deleteObject calls is very slow when I am deleting 15,000 objects.
How can I speed up this call? Are there things happening during the save operation that I can be aware of and avoid by setting parameters different or setting up my model another way? I've noticed that memory spikes during this operation as well. I really just want to "delete * from".
Thanks.
Supposing that you have in your Core Data model a parent and a child entities, and the parent has a to-many relationship to child called children, you should be able to delete all of the child objects without looping as follows:
NSManagedObject *parentObject = ...;
[parentObject setValue:nil forKey:#"children"];
or using the Core Data generated method
- (void)removeChildren:(NSSet *)value;
NSSet *children = [parentObject valueForKey:#"children"];
[parentObject removeChildren:children];
I am not sure if this will speed up the NSManagedObjectContext save operation. Please let me know about the performances.
Check the relationship dependency graph: a cascade of deletes triggered by the initial deletion will slow things down. If those deletes are unnecessary then change the deletion rule.
according to apple website : http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdCreateMOs.html
it is very simple :
Deleting a managed object is straightforward. You simply send its managed object context a deleteObject: message, passing the object you want to delete as the argument.
[aContext deleteObject:aManagedObject];
This removes the managed object from the object graph. Just as a new object is not saved to the store until the context is saved, a deleted object is not removed from the store until the context is saved.
I have an application that loads core data, then calls the XML Web Service to get updated data. I basically want my app, rather than to erase all the data and load everything (including new) to persist, I want it to add only the new stuff onto the existing stack (without duplication).
What's the general consensus strategy for something like this?
I fetch an NSSet* of all persisted objects and then perform an intersection operation on that set of NSManagedObject instances with a new managed object, which is populated with the data from an individual XML element and its contents.
If there is something left from that intersection, that means I have that element already in my data store. So I update the existing, already-persisted managed object's properties with data from the XML element and save, discarding the new managed object.
If I have an empty set, the newly created managed object gets saved into the data store directly.
I don't have a hash value available to compare between the persisted data and the XML data, so this works reasonably well.
If each item in your xml data set has a unique key, you can just use that key to find the existing record and update it with the new info
Like Corey says, find a unique key to use in your data. Search the original data for that key and if it's not found, the object is a new one and can be inserted. If the object is already in the original data, discard it.
If there's a chance that the data will be updated over time (objects getting updated on the web side) then you'll have to compare the old (iphone) object and the new object with the same key. If they're the exact same, discard the new one. If not, replace the old one.
If there's no unique key you can use, you're going to have to do this comparison on all objects. I'm not sure of the quickest way to compare two managed objects, but you could try creating a managed object with the new (web) data and comparing it with each of the objects in the original.
Im using a Core Data model for my iPhone app. I have been looking for a way to instantiate or use an Entity outside the ManagedObjectContext. (This should not be done, I know, Im also more looking for a way to not do that, but get the benefits anyway).
My challenge is that I have a view where the user can search for "Persons", all the search results are parsed and put into a Person managedObject then displayed in a list.
If the user clicks a Person from the list, then and only then would I like the Person entity to be persisted to the store, however this requires me to delete all the other results so they don't get persisted along with the desired one. Also to the best of my knowledge, if the user decides to quite the app, the store is persisted, potentially with all current search results mixed in with real user data!
Is there some way I could have a TempPerson NSObject I could use for the search list? Without, however, me having to manually pull the 45 attributes from the temp object and manually set them on the managedObject!
Sort of like:
NSManagedObject aPersonCorrectlyReturnedFromTheStore = (NSManagedObject *)tempPersonOfJustTypeNSObject
I have seen example code from Apple where they build a temporary store to facilitate undo/redo and other stuff on an object that is not yet persisted. This I feel would be overkill in my situation. I just need to display search results until the user selects a Person to persist.
Hope it is clear what Im trying to do, feeling like my Core Data vocabulary isn't quite large enough yet:)
Thanks for any suggestions.
You could create each temporary person object as an NSDictionary or NSMutableDictionary. You can then create a new Person managed object and use the fact that NSManagedObject instances are KVC compliant and use setValuesForKeysWithDictionary:.
New managed objects that are inserted are not actually persisted until you send the managed object context a save: message.
Keep track of them in a collection (set or array) -- you are probably already doing this since you are presenting the search results somehow. Then, delete (deleteObject:) them all except for the one(s) that the user selects.
The deleted managed objects will never be stored.
I have an iphone app with 2 managed object contexts. One of my contexts deals with a picker, which allows the user to add new records and then select one of those new records. Once the picker is hidden, that managed object context is saved and discarded.
I then want to use this selected object in my second managed object context, and add (relate) it to another object. This second MOC's changes may be saved or deleted. This is why the first MOC is created to allow the changes made in the picker to always be saved no matter wether they save or discard the changes in the second MOC.
I hope this is clear! My problem is that when the user selects the object from the picker, this object resides in a different context to where it needs to be used. Is there a way you can pass objects between contexts? Perhaps using the object's ID (after it has been persisted).
Thanks for your help!
You mentioned the correct solution in the question. You cannot pass NSManagedObjects between multiple contexts, but you can pass NSManagedObjectIDs and use them to query the appropriate context for the object represented by that ID. So simply persist out the data (via a save:), and then pass the ID to the other context and use it to ask the context for the appropriate object.
Depending on what you want to do you may want to rig up the mergeChangesFromContextDidSaveNotification: so that changes in one context are automatically reflected in the other.