How to work with temporary objects CoreData? - cocoa-touch

I use lib MagicalRecord (https://github.com/magicalpanda/MagicalRecord) for CoreData.framework.
I don't understand how to work with temporary objects.
How to create NSManagedContext for temporary objects and whether to delete each NSManagedObject after closing controller?

All objects created on a context are temporary objects and they become permanent when you save that context. So to discard them, you just don't save that context.
To create a new (temporary) context assuming you use Apple's Core Data Stack:
NSManagedObjectContext *tempChildContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
tempChildContext.parentContext = self.appDelegate.managedObjectContext;
To save changes, you need to do two saves, one on the temporary context and then push it into the main context.
[tempChildContext performBlock:^{
// do something that takes some time asynchronously using the temp context
// push to parent
NSError *error;
if (![tempChildContext save:&error])
{
// handle error
}
// save parent to disk asynchronously
[self.appDelegate.managedObjectContext performBlock:^{
NSError *error;
if (![self.appDelegate.managedObjectContext save:&error])
{
// handle error
}
}];
}];
I am sorry, I don't remember how to do it with MagicalRecord, but MR is just a wrapper around CoreData, so it will work. I stopped using MR on my first CoreData project. I suggest you read this: Multi-Context CoreData.

Related

NSManagedObjectContextObjectsDidChangeNotification NSDeletedObjectsKey Objects no longer has references to related objects

I want to respond to changes of certain NSManagedObjects that have been added, updated, or deleted.
I have an issue with the deleted objects, though: all relationships to other objects are now nil.
Is there a way to get this kind of notification before the object is affected this way?
Edit:
This is basically my delete code:
[moc deleteObject:myObject];
id saveBlock = ^{
NSError *error = nil;
BOOL saved = NO;
saved = [self save:&error];
// error handling.
};
[moc performBlockAndWait:saveBlock];
If you are wanting to react to deletions then you should be listening for NSManagedObjectContextWillSaveNotification and watch for the NSDSeletedObjectsKey come through as part of the notification. That is the last chance before deletion to deal with them.

Use NSManagedObject's own context property to delete it?

It may be kinda naive but I was wondering if it is correct to use the following statement to delete a managed object from the persistent store of Core Data:
[managedObject.managedObjectContext deleteObject:managedObject];
I ran the above in Xcode debugger - it didn't complain but the object's content was still there. Could it be that context was referenced through the object to be deleted, and thus causing a memory lock preventing deletion of the object?
In regards to your content persisting, you still need to call save: on the context after deleting the object.
I can't answer specifically if you will have an issue by referencing the managedObjectContext in the managedObject as I usually use a 'DataManager' to manage my managedObjectContext. Below is an example of a delete method that I used in one of my dataManagers:
- (void)deleteReport:(Report*)aReport inContext:(NSManagedObjectContext*)context {
if (aReport != nil) {
if (context == nil) {
context = self.managedObjectContext;
}
context.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy;
[context deleteObject:aReport];
NSError *error = nil;
[context save:&error];
if (error) {
//NSLog(#"%#", error);
}
}}
EDIT: For clarification, the Report in this method is an instance of NSManagedObject, and the method takes NSManagedObjectContext as a parameter, because the application that it was pulled from supports the use of multiple contexts.

How does the save method in Core Data works?

I'm following a guid in core data, and they implement an action method to preform saving to the database using a ManagedObject. I understand all the code in the method except the method which they say preform the saving, and to me it looks like the method checks if there is an error, and if yes so there is an NSLog to print that there was an error. this is the method:
- (IBAction)save:(id)sender {
NSManagedObjectContext *context = [self managedObjectContext];
// creating a new managed object
NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:#"Device" inManagedObjectContext:context];
[newDevice setValue:self.nameTextField.text forKey:#"name"];
[newDevice setValue:self.versionTextField.text forKey:#"version"];
[newDevice setValue:self.companyTextField.text forKey:#"company"];
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
Obviously something happens in [context save:&error] this call which I'd love if you can explain what?
Calling save: persists changes made to the object graph on the specific context and takes it one level above.
Each context contains its own changeset, and when you call save:, the changes are either taken one level above (to its parent context), or, if there is no parent context, to the store coordinator to be persisted by the method specified when opening the coordinator (SQLite, XML, binary, etc.).
Changes can be modifications, insertions or deletions.
Before saving, changes to objects are verified and objects are notified about the save process.
After saving, notifications are sent to the system to let know various components (such as fetch results controllers, your code, etc.) that the save operation has taken place.

Cancelling NSManagedObjectContext performBlock

I am using parent/child concurrency pattern to import large data chunks. Importing is performed in the background without blocking the main thread, like this:
NSManagedObjectContext *temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
temporaryContext.parentContext = mainMOC;
[temporaryContext performBlock:^{
// import data …
// push to parent
NSError *error;
if (![temporaryContext save:&error]) {
// handle error
}
// save parent to disk asynchronously
[mainMOC performBlock:^{
NSError *error;
if (![mainMOC save:&error]) {
// handle error
}
}];
}];
Everything works great, but what if I need to cancel data import? Is there any way to cancel performBlock?
No - blocks and other any synchronous operation can not implicitly be cancelled.
you have to program it to be cancellable
e.g. here maybe... split the performBLock up into N calls That each only do little work.
If anyone else has the same problem my solution was to use two independent managed object contexts which are both connected to the same persistent store coordinator. The managed object context that does the heavy lifting is encapsulated in a NSOperation subclass. NSOperation can be canceled at any point. Here is a link to the example provided by Apple.

iOS: Core Data class method

Is it possible and practical to create a Core Data class method that will return the current instance of managedObjectContext? I am wondering so that I can segue to other controllers and load modal views without having to pass the managedObjectContext.
Also if I am using Core Data with dispatch_async I know I need to create my own instance of managedObjectContext but I can use the same coordinator. Will this make the information accessible both inside the dispatch_async and in the main thread?
I am basically using the dispatch_async to get data from the API and store it while the user is using the application.
In the past, I've created a Core Data manager singleton class that has simplified things. Here is an example, but this is pre-iOS5/ARC, so some changes need to be made.
I had a similar issue when trying to asynchronously getting data from my server to the app. My method is a bit different, but basically here it is (this is a 4.3 project, so no ARC):
The following methods are in my DataUpdater singleton. This first method is called at app startup:
- (void) update { //download the updates on a new thread
[NSThread detachNewThreadSelector:#selector(updateThread)
toTarget:self withObject:nil];
}
It initializes a thread with this selector, which is responsible only for downloading the content from the API, then passing it back to the main thread to be saved.
- (void) updateThread { //the actual update thread
//New thread, new auto-release pool
//(dunno if you need to do anything fancy for ARC)
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
//...
//YOUR CODE TO DOWNLOAD (BUT *NOT* SAVE) DATA FROM THE SERVER
//DON'T CREATE ANY MANAGED OBJECTS HERE
//...
//Pass the data to the main thread to perform
//the commit to the Core Data Model
[self performSelectorOnMainThread:#selector(saveUpdate:)
withObject:data waitUntilDone:NO];
//kill the thread & the auto-release pool
[NSThread exit];
[pool release];
}
Now that we're back on the main thread, the data is added to the Core Data Model and then the context is saved.
- (void) saveUpdate:(NSArray *) data {
//add the objects to your Core Data Model
//and save context
NSError * error = nil;
[[[CoreManager defaultCoreManager] CoreContext] save:&error];
if (error) {
[NSException raise:#"Unable to save data update"
format:#"Reason: %#", [error localizedDescription]];
} else {
[[NSNotificationCenter defaultCenter] postNotification:
[NSNotification notificationWithName:#"DONE" object:nil]];
}
}
Dealing with the first part of the question only (you shouldnt really ask multiple questions!) you don't have to pass the managed object context around - presumably you are passing a managed object? In that case the context is available as a property of the managed object itself - .managedObjectContext.