NSManagedObjectContext passed to NSWindowController becoming nil - objective-c

I'm really scratchng my head trying to work out where my managed object context is vanishing to.
I'm originally instantiating it within my app delegate and then passing it into a retained property within an NSWindowController as such:
self.TPWC = [[TestPanelWindowController alloc] initWithWindowNibName:#"TestPanel"];
self.TPWC.managedObjectContext = self.managedObjectContext;
self.TPWC.persistentStoreCoordinator = self.persistentStoreCoordinator;
[TPWC.window makeKeyAndOrderFront:nil];
I've then got a button that should instantiate an NSManagedObject and insert it into the managed object context like this:
NSManagedObject *newInstanceOfSomeEntity =
[NSEntityDescription insertNewObjectForEntityForName:#"SomeEntity"
inManagedObjectContext:self.managedObjectContext];
At this point, self.managedObjectContext has somehow become nil.
I've inserted a breakpoint into windowDidLoad and I can confirm that at that point, we do have a valid instance of an NSManagedObjectContext, but it's somehow become nil in between viewDidLoad and then trying to insert a managed object.
I've tried creating a custom initialiser to set the NSManagedObjectContext but it's still becoming nil.
Core Data is quite new to me and I'm struggling to understand what's going wrong.

This is not a direct answer to why you're seeing your context disappear, but it could still fix your problem:
From an architecture standpoint, you really shouldn't be passing around the managed object context between objects, especially UI objects.
Instead you should have a global singleton class that instantiates the managed object context (along with the coordinator and persistent store, probably), and then provide access to it via a public property. Then, from your window controller, you would just access it from the singleton object.
(A side note if you're using multi-threading, be careful of accessing and using the same context from different threads.)

Related

Core Data attribute changes to nil (ARC related?)

I have some Core Data functionality that was working fine until some recent (seemingly unrelated) changes were made. Now I'm getting problems where all the attributes belonging to a particular NSManagedObject subclass instance are suddenly returning nil.
Let's say my NSManagedObject subclass is called Foo and it has only one attribute called value. Once I realised value was somehow becoming nil I went and setup the following category to monitor changes to value.
#implementation Foo (Debug)
- (void)setValue:(NSDate *)value
{
[self willChangeValueForKey:#"value"];
[self setPrimitiveValue:value forKey:#"value"];
[self didChangeValueForKey:#"value"];
}
- (NSDate *)value
{
[self willAccessValueForKey:#"value"];
NSDate *value = [self primitiveValueForKey:#"value"];
[self didAccessValueForKey:#"value"];
return value;
}
#end
setValue: is called for my object and the argument passed in is a non-nil NSDate. Then the value is retrieved (in another method). The same value that was specified is retrieved correctly.
However when another method tries to read value, the value accessor is called and a nil value is returned by primitiveValueForKey:.
In between the two reads setValue: is not called and the Foo object itself is still valid (non-nil). In fact no other Core Data operations are performed between the two reads on any Core Data object or the context as a whole.
We're using ARC in our project. Is it possible ARC is somehow messing with my Core Data variables and deallocating them? If so does anybody have any suggestions for debugging ARC deallocations? Or better yet, does anyone know a way to ensure ARC doesn't deallocate my variable.
This may not even be ARC related, however I'm at a bit of a loss as to what is going on. Any suggestions would be very much appreciated.
This is very likely because the NSManagedObjectContext that these objects belong to, is going away. When you have NSManagedObject instances around but you're not holding on to the context yourself, those managed objects will start returning nil.
Under ARC, make sure you store the context in a strong variable, i.e. an instance variable that's not weak or a static global.
Non-ARC, i.e. retain-release code, make sure you're retaining the context.
As others mentioned (it was my case also), be sure that you haven't reset your managed object context because if you do, all Entities stored as properties will have data: <fault>.
If you do reset your managed object context, you will also have to re-fetch the Entity itself.
check the viewDidLoad-Method
profile = [NSEntityDescription insertNewObjectForEntityForName:#"MyProfile" inManagedObjectContext:profileContext];
hope this works

Execute NSFetchRequest on application startup

In another question ( Accessing an NSApplications delegate in another class? ) I asked about calling the Application's delegate because I needed it's managedObjectContext for a fetch request. However, when I try to let all values be displayed in an NSTableView on application startup, I'm running into problems. DataController, my NSTableViewDataSource, calls it's init-method before my application delegate calls it's applicationWillFinishStartup or any other method to initialize the managedObjectContext. What am I doing wrong? How else can I fill an NSTableView with already existing objects?
You should access managedObjectContext only via its getter, even from DataController, as in [appDelegate managedObjectContext] or appDelegate.managedObjectContext.
Your managedObjectContext method should automatically set up the managed object context; you shouldn't write an explicit moc setup routines in your applicationDidFinishLaunching, etc. And the standard core-data template is written that way.
Now, for this to work, the app delegate needs to be properly set up from the point of view of DataController. However, init is called before all the IBOutlet is set up, so that's the wrong place to perform setup operations of objects inside the nib. Instead, use awakeFromNib to do these things. awakeFromNib is sent to every object after the IBOutlet etc. are all set up.
That said, writing your own DataController is a total waste of time. Just instantiate the standard NSArrayController in the nib file, and use it in the Core Data mode via binding. There's absolutely no need for you to write the fetch request by yourself. Study Apple's own CoreData sample codes and then google "Binding CoreData Tutorial" for many tutorials available on-line.

Retaining Managed objects - more general retaining objects

A quick question regarding Managed Objects.
I created an Array with Managed Objects (in Object 1: TableViewConbtroller), and pass one of those objects to another class/object (object 2: TableCell).
The original array should still be retained in the original caller class.
Then Object 2 is released, does that mean that that particular item in the array is released as well, as the reference to it in Object 2 was released?
I am trying to better understand how to work with ManagedObjects as I get 'Object was released' errors.
[EDIT]
After some experimenting I came across the following scenario:
I have the main AppDelegate.
In a different class I create an AppDelegate to obtain the ManagedObjectContext.
appDelegate = (iDomsAppDelegate *)[[UIApplication sharedApplication] delegate];
[self setContext:[appDelegate managedObjectContext]];
When the class is finished, and I release it, the variable in the class 'appDelegate' is also released. But then the ManagedObjectContext is closed, and obvious any future attempt to use it will cause a crash. So should I leave the appDelegate unreleased?
This comes to the same question as the above about when and how to release in those situations where an objects is used from another class. I think a way of putting it is, how to know when you own an object and when not.
The UIApplication retains your app delegate, so releasing it in your view controller's dealloc method will not deallocate the app delegate.

Memory management, and async operations: when does an object become nil?

I have a view that will be displaying downloaded images and text. I'd like to handle all the downloading asynchronously using ASIHTTPRequest, but I'm not sure how to go about notifying the view when downloads are finished...
If I pass my view controller as the delegate of the ASIHTTPRequest, and then my view is destroyed (user navigates away) will it fail gracefully when it tries to message my view controller because the delegate is now nil?
i.e. if i do this:
UIViewController *myvc = [[UIViewController alloc] init];
request.delegate = myvc;
[myvc release];
Do myvc, and request.delegate now == a pointer to nil?
This is the problem with being self-taught... I'm kinda fuzzy on some basic concepts.
Other ideas of how to handle this are welcome.
update: Looking at the source code for ASIHTTPRequest, it does not retain its delegate, so your code will either have to ensure that the delegate has not been released, or set the request's delegate property to nil before releasing your controller.
If you are going to have several asynchronous HTTP requests running, it may be a good idea to create an HTTPRequestManager class to act as delegate for all of your HTTP requests. This HTTPRequestManager class would remain in memory for the entire lifetime of your application, and it could take care of failing gracefully if/when your view controllers are released.
I would like to add a clarification about Cocoa's reference-counted memory management. It is important to remember that a released object is not automatically set to nil. Sending a message to nil is perfectly legal (and simply does nothing), but sending a message to a deleted object is undefined behaviour. Keeping a pointer to a deleted object is asking for trouble, and such living/dead pointers are referred to as zombies.

Checking for a valid delegate object before sending it a message

I am trying to implement the delegate Pattern in Objective-C, however I am experiencing a Bad Access exception when invoking the delegate sometimes. It seems this is caused by the delegate being released. Apple does not recommend to retain delegates.
How can I check my delegate if is still valid before trying to send it a message?
If there's a chance that the delegate will get released by the setter, then there's something wrong with your design. You should only set delegates on objects that have a shorter lifespan than the delegate itself. For example, setting a delegate on a subview/controller is fine, because the subview/controller has a shorter lifespan than the caller.
AFAIK, there is no reliable way to detect if an object has been released already.
What Apple means about not retaining delegates is that objects should not retain their delegates because they don't own them. These are only objects that handle messages.
That doesn't mean that you shouldn't retain delegates at all. The object that creates the delegate needs to own it. In the context of non-GC apps this means it should handle the retain and release cycle, and for GC apps, it means that the controller object keeps hold of a pointer to the delegate in an iVar.
without seeing some code or the error message, it is hard to find the root of this problem.
In a photoviewer application I'm using asynchronous http to load images; it happens that the user often dismisses the current view (referenced by my async http object through a delegate) before the http download completed causing a BAD_ACCESS when calling the view controller delegate method. I solved this by setting the .delegate to nil inside the dealloc block of the view controller
I'd like to share my experience also, which is very similar to Nico's one.
I've been working with a modified example of LazyTablesCode, wich is an example that comes direcly from Apple and loads images in a UITableView asynchronously. Communication between the downloader and the view it's made via delegates.
In my code, I had the problem that sometimes the load of the image finishes when the form that should be called through the delegate has been released. I've been forced to add this piece of code inside the code of the viewController (dealloc method):
if (self.nsDictionaryWithObjectsDownloading != nil) {
for (id theKey in self.nsDictionaryWithObjectsDownloading) {
Myobj *downloader = [self.nsDictionaryWithObjectsDownloading objectForKey:theKey];
downloader.delegate = nil;
}
}
It seems that these lines are solving the problem. Anyway It would be very appreciated opinions about if it's a good solution or not or even about memory issues when doing downloader.delegate = nil;
Thanks and greetings,