Should IBOutletCollections be set to nil in UIViewController viewDidUnload? - ios

My UIViewController class has an IBOutletCollection containing several IBOutlet objects. I am aware of the need to set retained outlets to nil in viewDidUnload, but do I also need to set the IBOutletCollection to nil? Or should it be released instead in dealloc? Or left alone altogether?

You need to set your outlets to nil in viewDidUnload in order to make sure that as much memory as possible can be released by the view controller when it receives a memory warning. In response to the memory warning, the view controller releases its view in order to free the memory the view (and all its subviews) is using. If you fail to release those outlets that you are retaining/holding a strong reference to, the subviews referenced by those outlets will not be destroyed and their memory won't be freed.
So yes, you should also set the property of an outlet collection to nil in viewDidUnload.
This requirement is independent of the responsibility to release all your retained ivars/properties in dealloc.

Related

Do system object delegates in ARC need to be set to nil?

An app crashes sometimes with error objc_object::release().
The Apple Developer Technical Support mentioned this:
Remember that you should always do something like _tableView.delegate
= nil; in your -dealloc methods, even if you are using ARC. For compatibility reasons system objects use unsafe_unretained
references to implement delegation, instead of the preferred modern
replacement weak.
Does that mean that I have to set the delegates of system objects to nil when the view controller is about to be released?
class MyViewController: UIViewController {
deinit {
tableView.delegate = nil
tableView.dataSource = nil
}
}
I always assumed UITableView and similar standard objects are using weak references to their delegates?
Update:
It seems that the example by the Technical Support was outdated as UITableView has already been updated to a weak delegate. However not all delegates have been updated, e.g. the AVAudioPlayer.delegate is still unowned(unsafe). It seems that Apple is gradually updating delegates to be weak.
So as to whether a delegate has been set to nil manually can simply be determined by inspecting the delegate declaration in Xcode. If it is weak, don't bother.
Yes you should set these delegates to nil.
As suggested by the name, unsafe_unretained references do not retain your view controller so there's no retain cycle or memory leak here. However, unlike weak, these references will not be set to nil automatically when your view controller is deallocated. In most cases this is not a problem as your view controller will outlive its views, or at least be deallocated at the same time.
Unfortunately there are a few cases where UIKit may have also temporarily retained the view. This can allow the view to outlive the view controller and attempt to call delegate methods on the deallocated object resulting in a crash.
The easiest way I know of to see this in action is to dismiss and deallocate a view controller which is a delegate of a scroll view (or one of its subclasses like UITableView) while the scroll is still scrolling (e.g. from a strong swipe gesture over a long list of items). The scroll view will then attempt to call delegate methods (like scrollViewDidScroll) on the deallocated controller.

Creating IBOutlets as ivars on ARC. Memory issues?

I'm aware best practices for Objective-C development says IBOutlets should always be defined as properties according to Apple.
From a practical perspective, in iOS and OS X outlets should be
defined as declared properties. Outlets should generally be weak,
except for those from File’s Owner to top-level objects in a nib file
(or, in iOS, a storyboard scene) which should be strong.
But for learning purposes let's say we got the following scenario using ARC:
#import <UIKit/UIKit.h>
#interface DetailViewController : UIViewController{
IBOutlet UIButton *buttonA;
IBOutlet UIButton *buttonB;
IBOutlet UIButton *buttonC;
}
#end
If I'm not mistaken, those three buttons are strong, so my question is: Will those buttons be released from memory once the ViewController is released?
Those buttons would be released automatically if they were weak, I know that, but not sure if they are strong.
Can anyone please help? Just to be clear, the method 'dealloc' on DetailViewController is empty.
You asked:
If I'm not mistaken, those three buttons are strong, so my question is: Will those buttons be released from memory once the ViewController is released?
Yes. Or, more accurately, those buttons will be deallocated when there are no more strong references. And in this scenario, those buttons now have two strong references, one being the view controller and another being the view to which these buttons were added as subviews. Both of those strong references would need to be relieved before the button would be deallocated.
But why would you want to maintain two strong references to that control? Generally you let the view maintain the strong reference to its subviews (i.e. let the view "own" its subviews), and the view controller is only using a weak reference to those subviews.
Those buttons would be released automatically if they were weak, I know that, but not sure if they are strong.
When the view controller has the weak reference, the buttons are being released because the only strong reference to the button is maintained by its superview and when that view is removed, then the button will lose its last strong reference and can be deallocated.
If the view controller has a strong reference, you are unnecessarily added another strong reference that needs to be relieved before the buttons are deallocated. You can do that, but it's unnecessary. You quoted from the Resource Programming Guide, but the preceding sentence says "you don’t need strong references to objects lower down in the graph because they're owned by their parents, and you should minimize the risk of creating strong reference cycles."
Bottom line, your example with the implicitly strong references to the IBOutlet controls will work fine. But there's no advantage to having the view controller maintain a strong reference to the buttons and it represents a bit of a misunderstanding of the object graph. View controllers really should only be maintaining weak references to the controls on their views.
Yes, when the ViewController is released, even its strongly referenced objects will be released. (Unless of course, some other non-released object holds a strong reference to them.)
There's a good discussion of weak and strong here: Explanation of strong and weak storage in iOS5

Is it possible that a weak reference might be garbage collected while I am using its parent in IOS?

Say I have a parent UIView. This UIView has a weak pointer to a subview UIImageView. If UIView is currently displayed on the screen, is it possible for the image view to be garbage collected since the parent UIview only has a weak reference to the UIImageView?
FYI UIview and UIImageView would both be on the screen
This UIView has a weak pointer to a subview UIImageView
Stop. If the UIImageView is a subview of the UIView, then the UIView has a strong pointer to the UIImageView. End of story.
In other words, a view retains its subviews. As long as the subview is a subview, it cannot vanish in a puff of smoke.
Now, on the other hand, if you remove that subview, then you have to worry about its memory management or it might vanish in a puff of smoke.
You use 'weak' property on subviews when they're expected to be created and directly added to a view subviews hierarchy : UIView addSubview: already keeps a strong reference to the view.
if your view is a weak property of a UIViewController, you typically create if from NIB, or in -(void)viewDidLoad. If the controller's view is discarded because not needed anymore, your subview will be too.
When you add a subview to your view, it will, by definition, maintain a strong reference to that subview. That's just part of the addSubview internal process of maintaining view hierarchies. The view will keep this strong reference to its subviews until that subview is manually removed (e.g., via removeFromSuperview) or if the superview, itself, is removed (e.g. you dismissed its view controller).
These strong references that views internally maintain on their subviews should not be confused with the weak references that you (or IB) might add to your view or view controller subclass .h file. Those weak references are just providing you a convenient way of referencing the subview or control. The fact that your .h file has a weak reference to the the subview, such as an IBOutlet for your benefit, doesn't alter the fact that views automatically already have a strong reference to their subviews behind the scenes as part of the overall view hierarchy.
So, a subview, such as an image view will only get deallocated when its last strong reference is removed (or in non-ARC language, when the retain count falls to zero). If you've added the image view as a subview, it won't get deallocated until that strong reference is eliminated (i.e. the image view is removed from it's superview, or the superview, itself is removed). Obviously, if you're maintaining other strong references to the control, those would have to be removed, as well, for the subview to be released.
A caveat: If you're not using ARC, i.e., you're doing manual reference counting (e.g. manually calling release and autorelease), it's certainly possible for you to make a programming mistake and "over release" the image view, thereby letting the retain count fall to zero, and thus allowing it to be deallocated prematurely. But given that you're referencing weak properties in your question, you must be using ARC, so this observation is moot.
There are other situations in which sloppy use of weak variables can cause problems, but I hesitate to elaborate unless you tell us that the subview is not showing up at all.
(By the way, you mention garbage collection (GC) in your question, but I assume the real question was "is it possible that my subview might be deallocated while ...". Obviously, iOS doesn't have GC. But I assume the real question is whether your subview might be deallocated while it's still present on its superview.)
If the UIVIew has the UIImageView added to it the reference will be incremented by that action wouldn't be released until it was removed from the view.
Without knowing why you asked this I would also say that I don't think there would be any harm in having a string reference to it because at the point where the UIView is cleaned up it would also dereference the the uiimageview and it would be released then.
Also I know you said garbage collected but iOS doesn't have garbage collection. I know what you mean though

To nil out outlets in a custom UIView's dealloc under ARC?

Usually in a UIViewController subclass, I nil out all outlets in viewDidUnload. But in my custom UIView, should I nil out those outlets (defined in my custom UIView) in dealloc instead? Does ARC perform these actions automatically?
No, still do it in viewDidUnload to get them out of memory as fast as possible.
ARC means you don't need to do it anywhere but it's still a bit more efficient if you do.
Though you don't ever need to nil properties in deadlock when using arc in any of your classes.

What's the best and safest practice for releasing outlets?

The iOS documentation says that your view controllers should release (and nil, since that's good practice and actually a must for 2.x compatibility) any retained outlets in -dealloc.
The documentation also says that you should do the same for your outlets in -viewDidUnload if you want to allow them being cleaned up when the view is not visible and a memory warning was issued.
I wonder about two things:
Does this mean that I should duplicate all my outlet releasing code? Put it all in -dealloc and in -viewDidUnload? That's a real pain to maintain and really easy to forget!
Am I guaranteed that all my outlets will be re-populated when the view is reloaded after a -viewDidUnload, including my outlets that reference non-view objects in my nib?
The ideal answer answers both questions above and if there is a distinction between which properties should optimally be released in -dealloc VS -viewDidUnload, clarifies this distinction in detail.
-viewDidUnload is strictly used for releasing IBOutlets with retain properties.
The reason for this has to do with the fact that UIViewController has a view property which it retains. That view property itself retains references to all of its subviews. These subviews are exactly what you are retaining inside these outlet properties. The problem lies in that these subviews have an "extra" retain on them.
The goal of -viewDidUnload is to clear up unnecessary memory usage. When -viewDidUnload is called, the view property has already been released, which releases the top level UIView along with all its subviews. Since we have retained some of these subviews however, they linger in memory, and we want to release them since they will no longer be used. New copies of these subviews will be created when (if) the view is reloaded. The properties are also set to nil, strictly so we don't have pointers pointing to deallocated memory.
In -dealloc all retained properties and instance variables should be released. In the case where the -viewDidUnload just executed, you will be sending a harmless [nil release]; to the IBOutlet retained properties you just set to nil.
To add to the excellent answers:
With the Apple LLVM 3.0 compiler (and ARC enabled), the need for -dealloc disappears (or at least, the need to release your outlets in it), leaving just -viewDidUnload to deal with. As a result, no more duplicate code.
Nick answered the part 1 of the question perfectly so I skip to part 2 right away.
You are guaranteed, indeed! When your view gets reloaded from a nib file, everything on that nib file gets reloaded. There is not method to partially load a nib file's contents so if that nib gets reloaded, everything inside will get reloaded, too. But everything loaded from a nib has an autorelease on it. So you have to retain them. If you have Outlets which retains these freshly loaded classes and you connected those Outlets as outlets on nib, you are good to go. On a view controller example controller itself is the file's owner of that nib, view is the root element on nib, view being retained by file's owner outlet "view", all other child elements retained by view itself. If you have multiple root elements or elements that root view does not retain then you have to be sure there is outlets for this elements to retain them if nib loading by system automatically, if you are loading nib by a method then you must be aware all those classes are autoreleased when they arrived to your code.
Simply views are classes too and when a nib file gets loaded, everything inside it gets loaded, not some part of it. It is your responsibility to manage outlets.

Resources