Clearing a context in Core Data: reset vs deleting registered objects? - ios

I was looking for posts regarding to this, but I don't fully understand... What is the difference between:
[context reset];
and:
for (NSManagedObjectID *objId in objectIds) {
[context deleteObject:[context objectWithID:objId]];
}
Or are they equivalent?
Thanks

Using reset puts the managed object context back to the state it was in when you first created it-- before you had performed any fetches, created any new objects, etc. If you have any managed objects in memory that were fetched from this context, they're now unusable. Using reset does not affect the persistent store file. All instances still exist afterward, they're just not in memory. They can be fetched again.
Using deleteObject removes the object from the persistent store. It does not exist any more. It can't be fetched anymore because it doesn't exist.

Related

When to init the NSManagedObjectContext?

I have an iOS app with master and detail layout.
In master, I managed my own NSManagedObjectContext, and also detail is, by this way:
NSPersistentStoreCoordinator *psc = ((AppDelegate *)UIApplication.sharedApplication.delegate).persistentStoreCoordinator;
_context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_context setPersistentStoreCoordinator:psc];
In master, I display list that can be clicked by user to show the detail in detail layout.
Upon filling the detail by user, user can save the detail by clicking on button there.
However, I am trying to understand this:
Since there is a save button in detail, the save method will save the detail with detail's context and call the load list in master
Load list will remove all the NSMutableArray of the list [_activities removeAllObjects]; and re-fetch the data from Core Data and reload the tableview
Done that but the the re-fetch function seems to use old data and not the latest.
Why does re-fetch the data doesn't work if I use same context?
You are using outdated APIs to create your managed object contexts. After creating a context, instead of assigning the persistent store, you should set the parentContext.
If you display a "list", you should be using a context that is of type NSMainQueueConcurrencyType. The best way is a NSFetchedResultsController which will also help you manage memory and performance, and greatly simplify the updates you need for your UI. (You would avoid the verbosity and complexity of merging the data "manually" via NSManagedObjectContextDidSaveNotification.)
NB: Resetting the context is a crude and inefficient method for the task you are trying to accomplish. Due to the fact that it affects the entire context, it could change the behavior in other parts of your app.
You have two contexts going on here which is why the data is not being refreshed. If you call [context reset] then you will find that the data will refresh.
Explanation:
An NSManagedObjedctContext is a "scratchpad" which manages the Objective-C representation of SQLite (or XML) data in memory. When you fetch data the context retains the managed objects that are created by the fetch. If you then fetch from another context that context reads the data from the persistent store and creates new managed objects based on what it finds.
ALSO: When you perform a fetch the context checks to see if a managed object representation is ALREADY in existence and then returns it if it is. This means that two contexts can get out of sync quite quickly.
There are several ways around this:
1) Calling reset on a context returns it to it's "baseState" which means all the managed objects are "forgotten" or released by the context. Therefore, another fetch will force the context to read the data directly from the store.
2) Implementing the NSManagedObjectContextDidSaveNotification (see here) will allow you to incorporate changes between contexts.
I have found this article very useful in the past.

MagicalRecord 2.3 Temporary Objects

sometimes I need to update an already persistend managed object with another temporary managed object created from a server response. The temporary object must be discarded and the other one should be saved immediately after the update operation. In MagicalRecord (MR) 2.3++ it is recommended to save objects like so:
- (void)updateObject:(NSManagedObject*)alreadyPersistedObject withDictionary:(NSDictionary*)dictionary {
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
NSManagedObject *updatedObject = [NSManagedObject MR_createEntityInContext:localContext];
[MyParserHandler parseDictionary:dictionary intoManagedObject:updatedObject];
// update some properties of alreadyPersistedObject using updatedObject
}];
}
I know that we should initialize temporary objects using temporary local contexts in earlier versions of MR. Is that also true for MR 2.3 and higher?
If YES, can somebody please give me a code example for that and needs the temp context be a child of the [NSManagedObjectContext MR_defaultContext] and how to discard this context after usage?
If NO, what is the preferred technique to do this now?
Thanks a lot for helping!
Just to restate the problem, you are trying to update existing data from an external data source into your core data store. You create a background context and have temporary objects into which you import this external data, and then save. So what you want to do is have those new changes propagate to your existing in-memory objects.
If this is the case, then if you simply use the body of that method, the default context provided by MagicalRecord will have those updates upon the completion of that block. MagicalRecord is trying to do much of that merging work for you. If you just create your temporary object inside the saveWithBlock: method, the save will eventually merge those changes to the default context. So, if your objects are in that context, they will receive those changes as a result of the save.
If you find that you need some more control over your merging of data, I suggest you use the built-in Core Data merging mechanisms rather than trying to merge manually. In this case, you should use the parent/child context relationships, or the NSManagedObjectContextDidSaveNotification to merge in changes from another context. The code for those solutions is fairly common on the internet.

Entities saved to RKManagedObjectStore's mainQueueManagedObjectContext disappear on next build

Using RestKit v0.20.0-rc1, I've managed to successfully create CoreData mappings and import objects from bundled JSON files and have the data persist for multiple builds. However, when I create my own entity and save it, the entity disappears immediately upon next build if I use [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext, but persists properly if I use [RKManagedObjectStore defaultStore].persistentStoreManagedObjectContext.
UserAccount *userAccount = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:managedObjectContext];
userAccount.userID = #(userID);
[userAccount addContactMethodsObject:phone];
NSError *error = nil;
if(![managedObjectContext save:&error])
NSLog(#"%#", error);
Using either managedObjectContext saves without errors in the above code, and any fetches from the same context returns the entity properly. But upon subsequent builds, fetches will always return nil if I use mainQueueManagedObjectContext, even though the above code is run on the main thread.
Is there anything I'm missing?
When you use save: on a context created using mainQueueManagedObjectContext, it won't persist its changes to the store. According to the documentation for RKManagedObjectStore (bolding mine):
The managed object context hierarchy is designed to isolate the main
thread from disk I/O and avoid deadlocks. Because the primary context
manages its own private queue, saving the main queue context will not
result in the objects being saved to the persistent store. The primary
context must be saved as well for objects to be persisted to disk.
If you want to persist your changes and still use a mainQueueManagedObject context, try using
- (BOOL)saveToPersistentStore:(NSError **)error
That will kick the changes up the context hierarchy.
The relevant documentation for RKManagedObjectStore can be found at http://restkit.org/api/latest/Classes/RKManagedObjectStore.html#//api/name/persistentStoreManagedObjectContext
The documentation for restkit's NSManagedObject category can be found at
http://restkit.org/api/0.20.0-pre3/Categories/NSManagedObjectContext+RKAdditions.html

Pointers to NSManagedObject after object delete / context save?

Can someone please explain what happens to the pointers to the NSManagedObjects after the object is deleted and the context is saved? How should I set them up so that they get set to nil automatically?
Well, it's quite simple.
[managedObjectContext deleteObject:managedObject];
[managedObjectContext save:error];
managedObject = nil;
If you are afraid of memory leaks when deleting lots of objects, just use fast enumeration. This is pretty much guaranteed to clean up behind itself:
for (NSManagedObject *obj in fetchedObjects) {
[managedObjectContext deleteObject:obj];
}
[managedObjectContext save:error];
After you delete an object, the isDeleted property will be true. After saving the context, the isDeleted will be false if you still have a reference to the managed object.
You can safely make weak references to managed objects. The weak reference will nil out automatically for you under ARC when Core Data is done with them.
Here are the three relevant paragraphs from the Core Data Programming Guide:
Core Data “owns” the life-cycle of managed objects. With faulting and
undo, you cannot make the same assumptions about the life-cycle of a
managed object as you would of a standard Cocoa object—managed objects
can be instantiated, destroyed, and resurrected by the framework as it
requires.
When a managed object is created, it is initialized with the default
values given for its entity in the managed object model. In many cases
the default values set in the model may be sufficient. Sometimes,
however, you may wish to perform additional initialization—perhaps
using dynamic values (such as the current date and time) that cannot
be represented in the model.
You should typically not override dealloc to clear transient
properties and other variables. Instead, you should override
didTurnIntoFault. didTurnIntoFault is invoked automatically by Core
Data when an object is turned into a fault and immediately prior to
actual deallocation. You might turn a managed object into a fault
specifically to reduce memory overhead (see “Reducing Memory
Overhead”), so it is important to ensure that you properly perform
clean-up operations in didTurnIntoFault.
I check for (managedObject.managedContext == nil).
If it is nil then it was deleted.
Although this is not guaranteed by Apple Documentation, it seems to be working fine with me.
If you use it in different contexts or it is not saved, it will not work.
See How can I tell whether an `NSManagedObject` has been deleted? for details.

Implementing CoreData using the parent/child approach with temporary objects

Question: What will be the best way to handle bunch of data that just some of the objects should be saved to disk in coredata?
This is the app/problem:
The app will provide users with the possibility to search for different items on the internet.
The search will return a number of objects that will be displayed to the user.
The user should be able to favorites any of these object at any time. Objects that has been favored should be connected to the current logged in user and live after the app has quit.
The app will have iOS6 as base.
I have been using using these resources
Apple's Core Data Programming Guide: Efficiently Importing Data
Implementing Fast and Efficient Core Data Import on iOS 5
iDeveloperTV CoreData performance course
Im currently looking into the parent/child approach with use of 3 Contexts: Master, Main and Confinement context types.
Current possible solution:
MasterContext that perform save on disk (Has the persistentStoreCoordinator)
MainContext that is used by the UI (child of the masterContext)
BackgroundContext to handle new objects from searches. (child of the mainContext)
So the user may do a search that will return 100 objects (imported on the background context and saved up to the main context).
2 of these objects are favored by the user (on the maincontext). The object will be added to the user and set as "should be saved". (On a save the objects will be pushed up to the master context)
When I save the mastercontext I dont want to save all the 100 objects to disk. Just the two objects the user have favored.
So im think about deleting the object that should not be saved to disk just before I do a save on the mastercontext.
- (void) mainContextHasSaved: (NSNotification *) notification {
NSLog(#"Lets save the master");
[_masterManagedObjectContext performBlock:^{
//Loop through all inserted object and check if they should be saved to disk
[self removeObjectThatShouldNotBeSavedToDisk];
NSError *error = nil;
BOOL saveSuccess = [_masterManagedObjectContext save:&error];
if(saveSuccess) {
//Do something
}
}];
}
But after what I understood is that when a save is performed on a parent context, all the changes will be propagated to the children. Then I will loose all the objects except the two that has been stored.
So does anyone know how to solve this kind of problem? Is it something I can do in the example presented above? Or should I create multiple persistentStores and move objects between contexts?
Thanks to all that is willing to help and if more information is needed just ask :)
In a similar project I used this solution which was also favored by the users:
Keep a time stamp attribute in the downloaded items and delete them when the time stamp is older than a certain threshold and they are not marked as favorite.

Resources