Releasing subviews


#1

I’m a little confused about the viewDidUnload and dealloc code on page 176. Our code never created or retained the subviews (nameField, serialNumberField, valueField, and dateLabel), so why do we need to release and zero the pointers? And if we do need to, doesn’t that imply the other subviews (the other labels we’re not bound to) are being leaked?

Or perhaps setting up the bindings retains the subviews?

I guess I just think it’s weird to see code releasing an object that I never alloced, copied, or retained. I’d appreciate a little more explanation if you can. Thanks.


#2

Blah, I went to eat lunch and the forum ate my post. I’ll try and explain it again.

When a XIB file is loaded, all of the archived objects are allocated and instantiated, using alloc/initWithCoder:. Then, all of these objects are sent the message autorelease.

After that, connections are made using setValue:forKey:, a method that every NSObject subclass has. So if an object named foo has an outlet named bar that has been connected to widget, the following code is executed:

[foo setValue:widget forKey:@"bar"];

setValue:forKey: first looks for a setBar: method. If it finds one, it invokes that, passing widget as the argument. If no such method is found, it automatically retains widget and assigns the ivar bar to point at widget. (If there is no ivar bar, an Unknown Key exception is raised. Read up on Key-Value Coding in the documentation for a more detailed explanation.)

Therefore, any object we have an outlet connection to is retained by the view controller. Furthermore, any view object that has a superview is retained by its superview. This means that nameField and friends are owned by two objects: the view controller and the view controller’s view. When a low memory warning occurs, the view is destroyed and all of its subviews are released. Any subview that was not also owned by the view controller is consequently destroyed, but, the ones we have connections to will still exist. These lingering views will never appear on the screen again, because when we reload our view from the XIB file, we will get brand new instances of nameField and friends.

Because a low memory warning means we are running out of memory, and we are never going to use these views again, we release the view controller’s ownership of them in viewDidUnload right away so that they are properly deallocated and we save some memory. We nil the pointers to them out because we NEVER want a pointer to a deallocated object for fear of sending it a message later and crashing.


#3

Another note:

I mentioned that all archived object are instantiated and then autoreleased and only continue to exist if they are retained by some other object via connection or superview/subview relationship.

This is still 100% true, but can be confusing when you look at MainWindow.xib because some extra stuff is going on. If you look at an application delegate object - which is instantiated from a XIB file - you will notice that the File’s Owner (UIApplication) has an outlet connection to the application delegate named delegate. That means that when this connection is made, setValue:forKey: is invoked and it will find a setDelegate: method for UIApplication and invoke that. What is odd here is that UIApplication’s delegate property is labelled as assign and therefore, the application delegate will not be retained by any objects in the XIB file.

But, of course, the application delegate obviously continues to exist throughout the entire time our application is running. So, what gives? Well, in this specific case, when UIApplication loads its XIB file, it actually retains all of the top-level objects in the XIB file. (Top-level objects being the ones that are in the Doc Window and not under a disclosure tab.) Therefore, both the app delegate and the UIWindow are retained. You can check this behavior by using Instruments’ ObjectAlloc tool to record reference counts for the app delegate and the window. You will notice that the window actually has a retain count of 2 (one from UIApplication retaining all its XIB objects and another from the connection from the app delegate to the window) and the app delegate has a retain count of 1.


#4

Thank you very much for your response. I know memory management is kind of voodoo, so it’s very helpful to know that there’s some retaining going on when I use IBOutlets.

If you don’t mind me asking, is this something I should think about when programming for the Mac, too? I know memory is much more abundant, so no one’s going to notice a couple of leaked NSTextFields, but I’d like to have clean code.

Thanks again for all your help.

/jgt


#5

It may seem like voodoo now, but I promise you’ll get it.

On the mac, xib files are unarchived in a totally different way and there are no low memory warnings, so this doesn’t apply.