NSCollectionView's setContent: memory management - objective-c

I have a NSCollectionView that I populate using [collectionView setContent:data]; where data is a NSArray filled with objects all of the same custom NSObject subclass. The item prototype of the collection view is a standard NSCollectionViewItem, not subclassed. The collection item's view is populated using bindings from my NSObject subclass.
Now to the problem, when analyzing my app using heapshots I see that there is a huge increase in memory when opening the window with the collection view. Instruments pinpoints this memory increase to the [collectionView setContent:data]; line. This memory is never reclaimed. Any ideas?
EDIT: I access the data object like this:
NSArray *data = [[[[MWWeatherController sharedInstance] cachedData] objectForKey:[NSString stringWithFormat:#"%u",index]] objectForKey:#"daily"];

Have you made the ViewController for the CollectionView KVO compliant?
Has the XIB for the CollectionView an ArrayController?
Bind the CollectionView of the XIB to the arrangedObjects of the ArrayController and set the Items through your ViewController method e.g. setMyCustomObjectsArray, that again sets the array that is observed by the ArrayController.
Make sure you release everything correctly in your custom object's dealloc method too.

I think you are not releasing data in your scope ... if you own the 'data' object make sure you release it.
Some query to answer it better --
How you allocate 'data'?
Who is releasing it?
[collectionView setContent:data]; more code snippet around this line

Related

NSManagedObject without values

Im passing a NSManagedObject to a UIView. So Im showing a UITableView of meetings brought from CoreData, if you tap on one of the meetings you will be able to see, on another view, more info of that meeting, info that is contained in a NSManagedObject. I want to pass that NSManagedObject to the view that will show its info.
So I created a init method of that view like this:
-(id)initWithMeeting:(NSManagedObject *)aMeeting{
_theMeeting = aMeeting;
return self;
}
Then I use the info in the _theMeeting object to show it in the view that I just created in the ViewDidLoad. The problem is that whenever I try to access any of the values of the NSManagedObject it crashes, it has values in the init but not in the ViewDidLoad.
I believe it has something to do with the Managed Oriented Context, but the Managed Oriented Context never disspears, is an attribute of the AppDelegate.
So I dont know how to pass that Object and keep it.
I also declared theMeeting:
#property(nonatomic, copy)NSManagedObject *theMeeting;
Hope you can help me.
Are you using the accessor to assign theMeeting? I think you're just bypassing it so aMeeting is not retained or copied, and therefore the crash.

Saving UIViewController in appDelegate

I am using Xcode 4.3 and need to know the parent view controller of the current view.
I am also using storyboard.
self.parentViewController always returns nil.
I saw different answers to save the parent view controller in AppDelegate class as a property. E.g., To create a variable: UIViewController parentViewController in AppDelegate and in the code write:
appDelegate.parentViewController = self;
I am afraid that it will consume memory - as this is a heavy object.
What is the best approach to know aretnViewController when using story board?
Whether or not an object is "heavy" does not matter as long as you store only a reference to it (in your case in the application delegate). Creating a second object would make a difference, but the line
appDelegate.parentViewController = self;
does not do that, it merely stores a reference to the object.
I know that this does not answer your direct question, but I think you should go ahead with the "store a reference in the app delegate" approach.

tableView:cellForRowAtIndexPath: not called because array is empty?

My tableView wasn't loading because tableView:cellForRowAtIndexPath: wasn't being called. The NSLog messages that I have written inside tableView:cellForRowAtIndexPath: did not appear in the console. I had a weak NSArray #property called listOfItems. When I changed it to strong, the tableView finally showed it's contents. Could someone please clarify if tableView:cellForRowAtIndexPath: wasn't being called because the NSArray was weak. Is there a connection between the listOfItems property and the tableView:cellForRowAtIndexPath: method being called?
No object had a strong reference to the array. Your view controller had a reference, but it wasn't strong. This means that you don't care if it gets removed from memory, you don't need it to stick around. In this case, since your table relied on the data in the array, you don't want it to be removed from memory: you want it to stay around as long as the table stays around. To do so, you create a strong reference, which makes sure that that object isn't being removed from memory until that pointer (your property, in this case) didn't point to it anymore.

how much to release in viewDidUnload

The Apple template provides this comment in the viewDidUnload:
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
So I typically set IB Outlets to nil in viewDidUnload then release them in dealloc. The question is, all my other retained ivar objects, some of which are views that were added programmatically, while others are data models, should they also be dealt with in these two methods? If not, why not?
from this answer here i gather that only view-related objects should go in viewDidUnload as =nil statements, which should probably include non-IB Outlet retained views, correct? Then, all other objects, included data models, should go in dealloc as release statements. Is this the normal practice?
viewDidUnload is called as a result of a low memory condition to unload the view for a view controller that is not currently visible. At this point the view object of the view controller has been released which means all the objects that are subviews of viewController.view have been released, but they are not deallocated if you are retaining them in your ivars.
You should release any object that will be recreated when the view is loaded again or things you can easily recreate as needed. The next time the view is used the view will be recreated either from the NIB or by calling loadView so all those things you release will be recreated.
When your view comes from a NIB all the view objects specified in the NIB are created and added as subviews of the view controller's view. Any ivars with IBOutlets are also connected to those subviews so that you also "own" those objects (you have a retain on them). You need to release those ivars so that they will actually get dealloc'd.
When your view is created programatically in loadView you should also release those object retained by your ivars that will be recreated in loadView the next time the view loads.
Same for anything you create in viewDidLoad (or viewWillAppear or elsewhere), such as data models, if you can recreate it "easily" later when the view loads again or when the object is needed then it should be released in viewDidLoad to reduce memory usage. Actually for non-view items, like a data model, I would release it in didReceiveMemoryWarning instead.
Assigning nil to a retained property using the setter causes a release to be sent to them, when you write self.myOutlet = nil you are invoking the setter method which is implemented something like this:
-(void)setMyOutlet:(id)newObject
{
[newObject retain]; // does nothing if newObject is nil
[myOutlet release];
myOutlet = newObject;
}

How does NSViewController avoid bindings memory leak? [have sample app]

I'm trying to implement my own version of NSViewController (for backwards compatibility), and I've hit a problem with bindings: Since bindings retain their target, I have a retain circle whenever I bind through File's owner.
So I thought I'd just explicitly remove my view from its superview and release the top level objects, and that would take care of the bindings, because my controller isn't holding on to the views anymore, so they release me and I can go away. But for some reason, my view controller still doesn't get released. Here's a sample app exhibiting the problem:
http://dl.dropbox.com/u/34351/BindingsLeak.zip
Build it, launch it, and hit Cmd-K ("Create Nib" in "Edit" menu) to load a NIB into the empty window. Hit Cmd-K again to release the first view controller (TestNibOwner) and load a new one. The old view controller never gets dealloced, though.
Remove the "value" binding on the checkbox, and it gets released just fine.
If you set breakpoints at the release/retain/autorelease overrides, you see that _NSBindingInfo retains the TestNibOwner, but never releases it in the leaking case.
Anyone know how to fix this?
Doing a little investigation with class-dump and friends, it looks like Apple has a private class called NSAutounbinder that takes care of this dirty work for classes such as NSViewController and NSWindowController. Can't really tell how it works or how to replicate it though.
So, I can't really answer your question on how to prevent the retain cycle from happening for arbitrary bindings in a loaded nib, but perhaps it's some consolation to know that Apple is cheating, and you're not missing anything obvious. :-)
One thing I've done for the same problem is to create a proxy NSObjectController inside my nib. My NSViewController-like class has a pointer to this proxy and all bindings are bound through it. When I want to cleanup the view controller, I then do [selfProxy setContent:nil] on the object controller and release the view controller. In this instance the NSObjectController proxy acts as the auto-unbinder in this case.
It's more manual and you can't just release the view by itself, but it does solve the retain problem.
I'd suggest you do this:
-(void) releaseTopLevelObjects
{
// Unbind the object controller's content by setting it to nil.
[selfProxy setContent:nil];
NSLog( #"topLevelObjects = %#", topLevelObjects );
[topLevelObjects release];
topLevelObjects = nil;
}
In your nib, bindings would happen through a path like:
selfProxy.content.representedObject.fooValue
When you remove your view from its superview, are you also sending it another -release message? It was created by unarchiving from the nib, right?