Really best practice to retain IBOutlet view elements? - iphone

I keep running into situations with UIViewControllers containing a large amount of IBOutlets connecting the controller to its view's subviews (typically UILabels).
Following "best practices", i.e. use retain on all UI elements: #property (retain, nonatomic) UILabel *theElement1, #property (retain, nonatomic) UILabel *theElement2, ... gives me insane amounts of boiler plate code in dealloc and viewDidUnload for the view controller.
The offending IBOutlets are never used nor set outside the UIViewController (the set-method is only used in viewDidUnload and when the nib is loaded) except automatically when the nib is loaded.
The result from "best practice" is:
dealloc littered with [theElement1 release], [theElement2 release] etc.
viewDidUnload with [self setTheElement1:nil], [self setTheElement2:nil] etc.
However, since all of those elements are known to be retained by the view anyway, and the view is released by the UIViewController at appropriate times, I see absolutely no reason whatsoever for me to manually manage this.
The reason for this particular "best practice" (as far as I can tell) is to be consistent with your retains. But once you start having a large amount of outlets, you're more likely to miss handling the some outlet somewhere in either of the two methods, than you'll have trouble correctly changing outlets to "retain" for those special outlets you really want to retain even after the view is goodbye.
Is there some reason for this "best practice" other than the one I know about or should I feel quite free to break this "rule" in the particular case of subviews to an UIViewController's view?

You should stick to this best practice. It protects you from very bizarre crashes when you access IBOutlets after a memory warning. Yes, you need to manually manage your IBOutlets. Accessorizer does a nice job of automating this code.
Before ObjC 2.0, we had to write all of our accessors by hand, too (#property and #synthesize are very new additions to the language). Things have gotten a lot nicer. As we move to the 64-bit ABI and garbage collection, things get even simpler (and you should expect these things eventually to make their way to iPhone).
But for now, follow the memory management rules as laid out in Memory Management of Nib Objects. You trade a really small amount of typing for a huge amount of debugging. (Hmm, looks like they've updated this doc again; time to study up myself.)

Related

Weak and strong properties in -viewDidUnload under ARC

I am new to iphone development . I am using ARC for my project. As far as I understood using ARC we don't have to release any object manually. But , I have observed in some places , people explicitly set their object to nil in the ViewDidUnload even after using ARC.
For example, in .h file I have something like this:
#property (unsafe_unretained, nonatomic) IBOutlet MKMapView *mapViewOutlet;
#property (unsafe_unretained, nonatomic) IBOutlet UIToolbar *toolBar;
#property (strong,nonatomic) NSMutableArray *dataArray;
And .m as follows:
- (void)viewDidUnload
{
[self setMapViewOutlet:nil];
[self setToolBar:nil];
[super viewDidUnload];
self.dataArray=nil;
}
My question is, is it really necessary to explicitly specify nil in the ViewDidUnload even under ARC?
The whole point of the viewDidUnload method is to release data that you don’t really need, in order to free memory. Read the documentation:
When a low-memory condition occurs and the current view controller’s
views are not needed, the system may opt to remove those views from
memory. This method is called after the view controller’s view has
been released and is your chance to perform any final cleanup. If your
view controller stores separate references to the view or its
subviews, you should use this method to release those references. You
can also use this method to remove references to any objects that you
created to support the view but that are no longer needed now that the
view is gone. You should not use this method to release user data or
any other information that cannot be easily recreated.
So you’re setting the properties to nil in order to release the objects now and help the system to free up some memory. But of course this depends on the property type – strong properties are “yours” and only you can decide whether to release them now (by setting to nil) or not. Weak properties could already be nil, for example if they pointed to some views that got released with the main view. And unsafe_unretained properties are a special beast. The object they point to might already been released, but that does not mean they were set to nil automatically. So you should either use one of the “safer” property types (strong/weak), or set the unsafe properties to nil here, to make sure you won’t use the released object later. There are no hard rules in this case, you have to think about the situation and what it means for the various properties.
By the way, viewDidUnload is getting deprecated in iOS 6, where no views are being released under low-memory conditions anymore. You still receive the didReceiveMemoryWarning callback, so that you can release some resources there if you want to. Again, I suggest that you read the documentation and run a few tests to see what happens and decide what you should do.
ARC will only release properties which do not hold a strong reference to an object. In your case, these are all strong references, so they will be kept unless they are explicitly set to nil.
The viewDidUnload method does not mean that your UIViewController is removed from memory, it simply means that its views are removed from memory (iOS Developer - ViewController lifecycle).
In this case, your UIViewController remains in memory, and therefore its properties as well, unless they are explicitly set to nil.
When you are using unsafe_unretained, you should assign it to nil because it will not be assigned to nil implicitly, where is case of weak reference it will be assigned to nil implicitly.So in order to avoid any dangling reference you need to assign to nil in case of unsafe_unretained.

Do IBOutlet member vars retain automatically?

Weird discovery, when I used a drag and drop to make a new IBOutlet, as shown below, not with a #property at all:
#interface SkinChoosingView : UIViewController {
IBOutlet UIActivityIndicatorView * activityIndicator;
}
Xcode inserted a -release and set the outlet to nil in viewDidUnload. I looked in viewDidLoad though, and no -retain was there! This went against everything I know about memory management.
I figure apple must know a thing or two about this stuff though, so is there something behind the scenes happening here? I have never had leaks from these types of IBOutlets, and I've never released them.
Yes, it automatically retains the outlet for you when loading the NIB file unless you explicitly declare the property associated with the outlet as an assigned property.
And since it retains the outlet for you, you must release in viewDidUnload as the outlets will be reloaded by the time next viewDidLoad is called.
The answer is that it uses "Key-Value Coading", which means it calls -setValue:forKey:, which has a "Default Search Pattern". For ivars, it does something like [ivar autorelease]; ivar = [newvalue retain];.
The "current best practice" is to stick IBOutlet on properties instead of ivars (see here). This makes it obvious what memory management pattern is being used and is more resilient to typos (e.g. if you misspell the ivar).

cocoa - quickly release all IBOUtlets

I have a view with many retained IBOutlets, loaded from a XIB.
I have read that they have to be released when the controller's dealloc method is called.
May I use a cycle to do this(also to avoid releasing each outlet separately)?
something like
for(UIView *v in self.view.subviews){
[v release];
v=nil;
}
?
also, after that, should I release the view as well?
That would only make sense if you've actually retained every single subview, and even then it seems like asking for trouble -- if you ever wanted to change your .xib, you'd have to remember to update you code as well. And there may also be non-view objects in a nib that can be connected to outlets, like view controllers.
There are only so many views you can fit on an iPhone- or iPad-sized screen, so it's hard to imagine that you have more than a few dozen outlets. I think the prudent thing to do here is to simply release each one separately. This is what and iOS developer would expect to see, and it won't be any slower than using a loop. It'll be more code, yes, but the code will be easy to understand.
self.view is automatically released on UIViewController deallocation, and subviews also, if not retained by your code.
You should re-read the chapter on memory management in Apple Documentation... ;)
I wrote like a releaseVars function, which takes address of variables, release them and set to nil. This save some lines of code, but still you have to manually take care of them.

iPhone: Interface Builder leaks memory?

I have been working on an iPhone project, where we created all the user interface programmatically in code. Now I'm going to start a new iPhone project and thinking of using Interface Builder instead, because it has been recommended to me as being a very useful tool, creating less headache than writing everything in code and in general much faster (regarding development time).
However, my team members have some concerns due to previous problems with using Interface Builder and resulting memory leaks. Therefor they suggest building everything in code again. I don't know where these concerns come from, but maybe someone with more experience than we have can give some insight on that topic.
Doing a simple Google search doesn't really provide any information proofing that there are any problems with memory leaks created by the Interface Builder itself.
By looking at the official documentation from Apple I only see these three things which I have to take care of:
#property (nonatomic, retain) IBOutlet UIUserInterfaceElementClass *anOutlet;
"You should then either synthesize the corresponding accessor methods, or implement them according to the declaration, and (in iPhone OS) release the corresponding variable in dealloc."
- (void)viewDidUnload {
self.anOutlet = nil;
[super viewDidUnload];
}
Anything I missed?
When you assign an IBOutlet via Interface Builder, that object is retained by the controller by default (even without explicitly setting a property with retain). Therefore, you need to either release all outlets, or set a property with assign.
This is a gotcha that gets most people, and leads to the most common of memory leaks. Especially on views that are repeatedly show and removed.

Memory Management of Interface Builder Outlets

I am using #property(nonatomic, retain) for my IBOutlets for an iPhone application. However, I'm not sure how to make sure I'm managing memory with them properly. The IBOutlets are all set up in Interface Builder, so I'm never calling alloc manually. This means that I'm not sure when and if to deallocate them or when to set them to point to nil.
What are the best practices ensuring that no memory is leaked once the view unloads?
If you use #properties for yourIBOutlets and make the connections in IB then your controller is essentially retaining the IB objedcts with the property and is it therefore responsible for releasing them when it's done with them.
When are you done with them?
In every case you should be setting your properties self.propertyname = nil in your viewDidUnload method and again in dealloc of each viewController.
It's quite straight forward, IB manages everything else.
By default, the semantics of #property is retain, meaning that when the view controller loads the nib and connects IBOutlets, they get retaind by the #synthesized setter. Follow the standard rules of Cocoa memory managment: you must release these properties eventually or you will leak memory. dealloc is probably a good place to do this. On the iphone you can do this via self.outletProperty = nil. On OS X (when you're not using GC), the rules are the same, except you can use [self->outletProperty release] explicitly, bypassing the #synthesized setter.
You should do it like this....
[yourOutletVar release];
yourOutletVar = nil;
Please don't forget to set the IBOutlets to nil finally, because it is necessary to get rid of dangling pointers issue.