Two NSArrayControllers bound to same NSManagedObjectContext - swift

I have two nib files each with its own window populated by data from the same Core Data Managed Object Context (MOC) but each bound to a different array-controller. The problem is that when I delete a data object in one of the window's array-controller, it persists in the other window's array-controller even after saving the common MOC and restarting the program. To permanently remove the unwanted data object, I must remove it at each window separately. This doesn't seem the way Core Data should work. Shouldn't array controllers using the same MOC have a common source of persistent data?

For my setup of the bound array-controller, it is not enough to simply connect the window's 'delete' button to NSControllerArray's 'remove:' method. Instead, I subclass this method and specify a direct MOC delete of the target data-object as follows:
- (void)remove:(id)sender
{
[MOC deleteObject:[[self selectedObjects] lastObject]];
}

Related

copy NSManagedObject to a new object in memory

I have a menu screen that populates with data from an NSManagedObject. While this screen is up the user can enter in all sorts of values. etc.
The problem I am having is that I need to not alter the initial NSManagedObject used to populate the screen. I need a copy that the user can alter instead because if they cancel out before everything is done, then what was the original and correct information gets corrupt by impartial data.
However, I am having issues implementing any kind of copy method or process inside my NSManagedObject because all of the properties are #dynamic.
If I just 'return self' in a copyWithZone method inside the NSManagedObject, would that properly copy everything?
Why not use a category to add a copy method to your custom NSManagedObject?
If your NSManagedObject was User, then create a new category User+copy.h/m where in you would add the copy method which would simply copy all the individual fields one by one.
What you want is to make your changes to the same NSManagedObject, but in a child NSManagedObjectContext. That way, if you need to discard the changes, you can just throw the child context away without affecting your main context or object. See this question.

UIManagedDocument and NSFetchedResultsController

I'm having issues with saving an entity using a UIManagedDocument. I have a NSFetchedResultsController with its context set as the UIManagedDocuments context. I have the controller set up with sections. The user adds an entry to the UIManagedDocuments context and I save the context using:
[context save:&error];
and my NSFetchedResultsController updates properly.
If I leave the view that has the NSFetchedResultsController and then return to the view, in which I create a new NSFetchedResultsController with the same UIManagedDocuments context, I get an error back when I call:
[fetchedResultsController performFetch:&error];
The error says:
CoreData: error: (NSFetchedResultsController) The fetched object at index has an out of order section name '. Objects must be sorted by section name'
and the fetch fails.
If I wait a while before I return to the view I don't get the error. I know that the save operation is done on the child context and then pushes the changes onto the parent context and I think this has something to do with the problem. Does anyone have a solution?
According to the UIManagedDocument Reference, you should not save via the NSManagedObjectContext. Saving should be done via the appropriate UIManagedDocument API(s). Unfortunately, the document is not exactly clear on what you should use.
If you use the undo manager, that path is supposed to make sure everything is appropriately marked as dirty, and saved. Likewise, using an explicit call to
[document updateChangeCount:UIDocumentChangeDone];
is supposed to provide similar functionality. However, saving will be postponed because it is done in a separate thread.

Why am I getting a merge error when saving an NSManagedObjectContext this 'clean'?

thanks in advance for any help. I've spent today battling this and I think that there is something seriously wrong with my understanding of how the framework works.
I'm working on a core data application where the entities have a parent/child relationship. The application creates an NSManagedObjectContext (MOC) on startup. When the application is run for the first time it uses an async block to import the contents of a plist into a second MOC (the root node is obtained from the main MOC using the URI and -managedObjectIDForURIRepresentation:), just before the block finishes it saves the second context.
In my data controller I subscribe to NSManagedObjectContextDidSaveNotification and the following code is run when the notification is sent:
- (void)backgroundContextDidSave:(NSNotification *)notification {
if(![notification.object isEqual:self.managedObjectContext]){
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:#selector(backgroundContextDidSave:)
withObject:notification
waitUntilDone:NO];
return;
}
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification]; ;
}
}
I've done a sanity check on this code and sure enough, when the second MOC saves, this is called from the thread executing the block, it is then deferred, and run from the main thread. The notification object contains all objects imported in the second MOC, including the two we're going to be dealing with next.
When this has finished I run the following code which is in a method of the NSManagedObject subclass the objects belong to, it is simply meant to remove a child from its parent:
TreeEntry *oldParent=self.parent; //keep a pointer to the old parent around so we can delete self from the children
// These next four lines are a sanity check to make sure that both objects are on the same MOC we're saving
NSManagedObjectContext *selfContext=self.managedObjectContext;
NSManagedObjectContext *parentContext=self.parent.managedObjectContext;
NSManagedObjectContext *sharedContext=[[DataController sharedDataController] managedObjectContext];
assert([selfContext isEqual:parentContext] && [selfContext isEqual:sharedContext]);
// now we fault the two objects to make sure we can not possibly have them or any changes
// to them in the state of the main MOC, by this time the second MOC is long gone
[sharedContext refreshObject:self.parent mergeChanges:NO];
[sharedContext refreshObject:self mergeChanges:NO];
// up to this point, sharedContex.insertedObjects, sharedContext.updatedObects and sharedContext.deletedObjects
// have all contained no objects at all. None of the above was necessary as the MOC held no changes at all
[sharedContext saveChanges]; // we save it to, well, just to make sure I guess, I may be going crazy
// Now we carry out two changes to the objects, problem occurs if only one change is carried out,
// I'm showing both to show that there relationship is being kept consistent and valid
self.parent=nil;
[oldParent removeChild:self];
// When the next line is run the save fails with a merge conflict
[sharedContext saveChanges];
The last save fails with a Cocoa error 133020, which is a merge fail. The two NSMergeConflicts in the error relate to the entries we're dealing with (self and self.parent).
I just don't understand how that can be. The objects have no state when they are modified so they MUST be getting loaded from the store. Two simple changes are made and then when they are saved straight afterwards a merge conflict occurs. How can that be? nothing else has messed with the store and we've just loaded the objects from it.
I know I can change the merge policy but I don't want to do it without understanding what's going on.
Any ideas? I'm sure it's just that my mental model if what's going on is wrong, but I've not been able to set it right all day!
Ok, it WAS a fundamental misunderstanding on my part of how the framework works, or to be more accurate, the NSManagedStoreCoordinator cache.
When I save the background context the changes go to disk, but apparently the NSManagedStoreCoordinator (which both contexts share) doesn't update or invalidate its cache.
When I refresh the objects in the main MOC the data used to re-populate them comes from the cache, which still has the old data. It doesn't re-load from the disk. The solution was to use [MOC setStalenessInterval:0.0] to force re-loading from disk.

tableview coredata temporary object

I am using a tableview with data from coredata using nsfetchedresultscontroller. When the view loads i make a new entity using
SomeManagedObject *someManagedObject = [NSEntityDescription insertNewObjectForEntityForName:#"SomeManagedObject" inManagedObjectContext:self.managedObjectContext];
This way the new entity appears in my tableview. Now i want this entity to be only temporary, but when i edit some object inside the tableview and save the managedObjectContext the temporary entity will also get saved and i don't want that.
Is their a way to save one object only and not everything inside de managedObjectContext?
Is their some other way to make a temporary object for my tableview.
Any help would be very welcome.
Thanks
Ton
Create the new NSManagedObject with it's alloc init and pass nil instead of the NSManagedObjectContext. Then if you later decide you want that object to be permanent then set it's context. However this will not allow you to see it in a NSFetchedResultsController because it will not be associated with the context.
A better answer can be provided if you could explain what your ultimate goal is.
No, in a managedObjectContext saving is a all or nothing. What I do not know is what happens if you set the persistent store of the managed object to nil
- (void)assignObject:(id)object toPersistentStore:(NSPersistentStore *)store
If you then save the managedObjectContext this object should not be saved. It is just a guess, but tell me if it works ;-)
For temporary managed objects, create them with a 2nd managed object context (MOC). When you are finished, simply release the MOC without performing a save.
Look at the Adding a Book code in CoreDataBooks which uses the same approach to throw away the newly added object when the user cancels.

Core Data - How to check if a managed object's properties have been deallocated?

I've created a program that uses core data and it works beautifully.
I've since attempted to move all my core data methods calls and fetch routines into a class that is self contained. My main program then instantiates that class and makes some basic method calls into that class, and the class then does all the core data stuff behind the scenes. What I'm running into, is that sometimes I'll find that when I grab a managed object from the context, I'll have a valid object, but its properties have been deallocated, and I'll cause a crash. I've played with the zombies and looked for memory leaks, and what I have gathered is it seems that the run loop is probably responsible for deallocating the memory, but I'm not sure.
Is there a way to determine if that memory has been deallocated and force the core data to get it back if I need to access it? My managedObjectContext never gets deallocated, and the fetchedResultsController never does, either.
I thought maybe I needed to use the [managedObjectContext refreshObject:mergeData:] method, or the [managedObjectContext setRetainsRegisteredObjects:] method. Although, I'm under the impression that last one may not be the best bet since it will be more memory intensive (from what I understand).
These errors only popped up when I moved the core data calls into another class file, and they are random when they show up.
Any insight would be appreciated.
-Ryan
Sounds to me like you are not retaining objects you want to keep hanging around. If you are doing something like this:
NSArray *array = [moc executeFetchRequest:request error:&error];
you do not own the returned array and it will most likely disappear when the current autorelease pool is drained. This will occur when the run loop finishes processing the current event.
All this is speculation. If you want a proper answer, you need to post your code.
It's hard to know what the problem is based on your description, but you might want to look at the Core Data memory management guide. You shouldn't have to worry about memory management for managed objects and their entities (they're fetched and faulted automatically). When you talk about "properties," do you mean custom properties backed by ivars? If so, these should be released in didTurnIntoFault and allocd as needed (probably in the accessor).
I was struggling with a similar issue. I'm using a managed object class and want to set its properties dependent on user input. But the sometimes the properties and sometimes the whole managed object were deallocated.
After reading the Apple documentation http://developer.apple.com/library/IOs/#documentation/Cocoa/Conceptual/CoreData/Articles/cdMemory.html the chapter "The Role of the Managed Object Context" I learned that managed objects are released each run loop completes.
And there is the golden advice to set
[myMangedObjectContext setRetainsRegisteredObjects:YES];
(I had to set it in the init method (initWithNibName for me) of my view controller.)
You should also regard to retain only the objects you need to as explained in the documentation. But read it yourself.
If I'm not right please correct me.
I also made a class that handles all my CoreData fetching and stuff. I ran into a couple of gotcha's, so here are some tips. (If I am making any memory management errors in these examples, please let me know.)
Two things:
1) Made a "fetchFiredObject" method in the CoreData handler class. So when I want to get a managedObject that has all its variables and is a "fully feathered bird" so to speak, instead of doing:
aManagedObject *myManagedObject = [myCoreDataHandler.managedObjectStorageArray objectAtIndex:1];
int x = myManagedObject.someVariable.intValue;
instead I do:
aManagedObject *myManagedObject = [myCoreDataHandler fetchFiredObjectAtIndex:1];
int x = myManagedObject.someVariable.intValue;
And in myCoreDataHandler's fetchFiredObjectAtIndex:i method, we're going into the array, finding the object key at index i, then doing a fetchRequest for that object key, and returning the freshly-fetched managedObject so that it won't have been faulted or deallocated, etc. :D
2) When I create a new child viewController, I populate its "myCoreDataHandler" value from the parent upon creation. However, this happens on a subsequent line of code after the line of code that creates the new viewController. Therefore, any code in the child's viewDidLoad that tries to use myCoreDataHandler's methods will return empty objects because viewDidLoad completes before the parent's next line of code where it sets the values of globals in the child object. So make sure you are not accessing your "Core Data handling object" from within viewDidLoad or anything local methods called by viewDidLoad! Instead call them from the parent after creating the new viewController.