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).
Related
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.
While coding always the same questions concerning retain counts of IBOutlets came along: Retain count after unarchiving an object from NIB? When to use #property's for an IBOutlet? Retain or assign while setting? Differences between Mac and iPhone?
So I read The Nib Object Life Cycle from Apple's documentation. Some test apps on Mac and iPhone gave me some strange results. Nevertheless I wrote down a few rules how to handle this issue to stay happy while coding but now wanted to verify with the community and listen to your opinions and experiences:
Always create an IBOutlet for top-level objects. For non-top-level objects if necessary (access needed).
Always provide a property as follows for IBOutlets (and release them where necessary!):
Top-level objects on Mac:
#property (nonatomic, assign) IBOutlet SomeObject *someObject;
#synthesize someObject;
[self.someObject release];
Non-top-level objects on Mac (no release):
#property (nonatomic, assign) IBOutlet NSWindow *window;
#synthesize someObject;
Top-level objects on iPhone (must retain):
#property (nonatomic, retain) IBOutlet SomeObject *someObject;
#synthesize someObject;
[self.someObject release];
Non-top-level objects on iPhone (should retain):
#property (nonatomic, retain) IBOutlet UIWindow *window;
#synthesize window;
[self.window release];
Side notes:
On Mac and iPhone outlet connections are made with a setter if available.
Top-level objects: "have [...] no owning object"
Non-top-level objects: "any objects that have a parent or owning object, such as views nested inside view hierarchies."
So the question would be: is this correct and good practice?
I hope you can approve or correct it.
Always have your nibs' File's Owner be a subclass of NSWindowController or NSViewController (on Mac OS X) or UIViewController (on iPhone), and use #property (retain) IBOutlet for all of its outlets, doing appropriate releases in your controller subclass -dealloc method.
This pattern will work fine on both Mac OS X and iPhone OS, because NSWindowController and NSViewController on Mac OS X take implicit ownership of top-level objects for you (and relinquish that in their own -dealloc methods), and iPhone OS doesn't take any implicit ownership of top-level objects for you during nib loading.
Top-level objects: "have [...] no owning object"
Nix. Top-level objects are owned by the File's Owner, which is the File's Owner because it owns all the top-level objects in the file.
Windows have that option to release themselves as a convenience, but I find my design cleaner (even if it's a little more work) when I either turn it off and manage its lifetime myself, just like any other object I own, or use a window controller.
If you think this conflicts with the documentation you were quoting, let's go through the entire paragraph:
Objects in the nib file are initially created with a retain count of 1. As it rebuilds the object hierarchy, however, AppKit autoreleases any objects that have a parent or owning object, such as views nested inside view hierarchies.
Thus killing off its own ownerships. The nib loader doesn't want to own your objects.
By the time the nib-loading code is done, only the top-level objects in the nib file have a positive retain count and no owning object. Your code is responsible for releasing these top-level objects.
In other words, it's handing the ownership over to you.
The curious artifact of that is that you'll actually leak the object if your property to it has retain semantics. The documentation says you should retain it:
For both Mac OS X and UIKit, the recommended way to manage the top-level objects in a nib file is to create outlets for them in the File’s Owner object and then define setter methods to retain and release those objects as needed.
But if you do this, the object will remain alive even after you release your ownership of it.
I think I'll go file a bug about this. (Edit: Done. x-radar://problem/7559755) At the very least, the nib loader shouldn't be handing off two retentions, which it does in my test app (on 10.5.8 and 10.6.1).
From apple's doc mentioned above:
For both Mac OS X and UIKit, the recommended way to manage the top-level objects in a nib file is to create outlets for them in the File’s Owner object and then define setter methods to retain and release those objects as needed. Setter methods give you an appropriate place to include your memory-management code, even in situations where your application uses garbage collection. One easy way to implement your setter methods is to create a declared property (using the #property syntax) and let the compiler create them for you. For more information on how to define properties, see The Objective-C Programming Language.
Otherwise use #property(nonatomic, retain) IBOutlet * outletName;
I can write my opinion about iPhone NIB development:
If you use IB then use as many IBOutlets as possible (sometimes you don't know the views hierarchy when you build a NIB - it may be dynamic) or don't use them at all - otherwise there will be a mess
Use properties only if you want to access the views from outside the View Controller (if they should be public)
AFAIK there's no need to manage memory for IBOutlets
Hope it helps...
You should follow standard memory management guidelines. If your outlet is connected to a retained property, then you must release it in the -dealloc message.
And yes, you any top level objects not retained by any other objects usually need to be retained by yourself.
1) In general, why would you have a top-level object with no IBOutlet to point to it anyway? That requirement has never seemed very restrictive.
2) I think you got the settings about right for the iPhone. You can also use an assign property on the iPhone as well, which does what you would expect... but in general after a lot of use I prefer to use retain properties so I am 100% clear on when I consider the object released (especially with the viewDidUnload method to implement).
Also, just as a side note it's not good form to call [self.property release]. That leaves the reference intact but potentially invalid, if something else ever also releases the object... either say self.property = nil, or (better) set the underlying class variable to nil directly without using properties in dealloc statements (to avoid any possible side effects in dealloc).
As I mentioned in response to another poster, you can keep things clean by using IBOutlet properties declared in private class-local category extensions so they are not public properties. That looks like:
// in .m file
#interface MyClass ()
#property (nonatomic, retain) IBOutlet UIView *myPrivateView;
#end
#implementation MyClass
#synthesize myPrivateView;
.....
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.
In most examples I see the following setup of IBOutlets:
(Example A)
FooController.h:
#interface FooController : UIViewController {
UILabel *fooLabel;
}
#property (nonatomic, retain) IBOutlet UILabel *fooLabel;
#end
FooController.m:
#implementation FooController
#synthesize fooLabel;
#end
But this works also fine (notice: no property and no synthesize):
(Example B)
FooController.h:
#interface FooController : UIViewController {
IBOutlet UILabel *fooLabel;
}
#end
FooController.m:
#implementation FooController
#end
Are there any downsides of defining IBOutlets as in Example B? Like memory leaks? Seems to work fine and I prefer to not expose the IBOutlets as public properties as they are not used as such, they are only used in the controller implementation. Defining it in three places without a real need does not strike me as very DRY (Don't Repeat Yourself).
On Mac OS X, IBOutlets are connected like this:
Look for a method called set<OutletName>:. If it exists call it.
If no method exists, look for an instance variable named <OutletName>, set it without retaining.
On iPhone OS, IBOutlets are connected like this:
call [object setValue:outletValue forKey:#"<OutletName>"]
The behavior of set value for key is to do something like this:
Look for a method called set<OutletName>:. If it exists call it.
If no method exists, look for an instance variable named <OutletName>, set it and retain it.
If you use a property, you'll fall into the "Look for a method called set<OutletName>:..." case on both platforms. If you just use an instance variable, then you'll have different retain/release behavior on Mac OS X VS iPhone OS. There's nothing wrong with using an instance variable, you just need to deal with this difference in behavior as you switch between platforms.
Here's a link to full documentation on just this topic.
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html#//apple_ref/doc/uid/10000051i-CH4-SW6
On Mac OS X, IBOutlets are not retained by defaults. This is the opposite of the behavior on iPhone OS: on iPhone OS, if you don't declare a property it is retained and you must release this property in the dealloc method. Additionally, the 64-bit runtime can synthesize instance variables using property declarations. That means that someday the instance variables (with the IBOutlet) may be omitted.
For these reasons it is more homogeneous and compatible to create always a property and use the IBOutlet only in the property. Unfortunately, it is also more verbose.
In your first example you always have to release the outlet in the dealloc method. In your second example you must release the outlet only with iPhone OS.
The end result is exactly the same, but you have to keep a few things in mind:
When using instance fields as outlets, you should NOT release them in dealloc.
When using properties that have the (retain) attribute, you have to release the property in dealloc (using self.property=nil or by releasing the backing variable). This makes it a lot more transparent as to what's going on.
Actually it all comes down to the same old rule: "thou shalt release what you alloc/retain". So in case you use an instance field as outlet, you didn't alloc/retain it, so you shouldn't release it.
Its possible that those examples use the retain because the sample code is programmatically allocating and initializing a UILabel and then adding it to the UIView. That's the case for many examples, since learning how to use Interface Builder is often not their point.
The second example (no property and no synthesize) with the IBOutlet is used when the developer 'assigns' the UILabel (Button, View, etc) within the Interface Builder -- by dragging the IBOulet to the Label or other View component. In my opinion, the preceding drag and drop action (Label onto View) also add the subview, the Label to a View -- and so on. Label is retained by a View; a View is retained by Window; Window is retained by File's Owner. File's Owner is usually your Document that is booted up in main.
You will note that when you step through your program (by adding an awakeFromNib
- (void)awakeFromNib
{
[fooLabel blahblah];
}
that fooLabel already has a memory address.
Thats because the Label was initialized from a file bundle (the nib file) using not init but initWithCoder. Which essentially deserializes the filestream to an object - and then sets the IBOutlet variable. (We're still talking about the IBOutlet method).
Also note that the aforementioned iOS method uses the Key Value method
call [object setValue:outletValue forKey:#"<OutletName>"]
which is the Observer/Observable pattern. That pattern require the Observable object reference each Observer in a Set/Array. A change in value will iterate the Set/Array and equally update all Observers. That Set WILL already retain each Observer thus the lack of retain in iOS.
Further and the rest is speculation.
It seems that cases when you do use Interface Builder then
#property (nonatomic, retain) IBOutlet UILabel *fooLabel;
should possibly be changed to
#property (nonatomic, weak) IBOutlet UILabel *fooLabel;
or
#property (nonatomic, assign) IBOutlet UILabel *fooLabel;
And then it needn't be released in a dealloc method. Plus it will satisfy the OSX and iOS requirements.
That's based on logic and I could be missing some pieces here.
Nevertheless, it may not matter if the view is persistent through the life of your program. Whereas a label in a modal dialog box (open, close, open, close) may in fact have over-retained and leak per cycle. And that's because (speculation again) each closed dialog box is serialized into a file system and thus persists x,y position and size, along with its subviews, etc. And subsequently deserialized ... on the next session open (Opposed to say minimiz or hidden.)
If I have something like a UILabel linked to a xib file, do I need to release it on dealloc of my view? The reason I ask is because I don't alloc it, which makes me think I don't need to release it either?
eg (in the header):
IBOutlet UILabel *lblExample;
in the implementation:
....
[lblExample setText:#"whatever"];
....
-(void)dealloc{
[lblExample release];//?????????
}
If you follow what is now considered to be best practice, you should release outlet properties, because you should have retained them in the set accessor:
#interface MyController : MySuperclass {
Control *uiElement;
}
#property (nonatomic, retain) IBOutlet Control *uiElement;
#end
#implementation MyController
#synthesize uiElement;
- (void)dealloc {
[uiElement release];
[super dealloc];
}
#end
The advantage of this approach is that it makes the memory management semantics explicit and clear, and it works consistently across all platforms for all nib files.
Note: The following comments apply only to iOS prior to 3.0. With 3.0 and later, you should instead simply nil out property values in viewDidUnload.
One consideration here, though, is when your controller might dispose of its user interface and reload it dynamically on demand (for example, if you have a view controller that loads a view from a nib file, but on request -- say under memory pressure -- releases it, with the expectation that it can be reloaded if the view is needed again). In this situation, you want to make sure that when the main view is disposed of you also relinquish ownership of any other outlets so that they too can be deallocated. For UIViewController, you can deal with this issue by overriding setView: as follows:
- (void)setView:(UIView *)newView {
if (newView == nil) {
self.uiElement = nil;
}
[super setView:aView];
}
Unfortunately this gives rise to a further issue. Because UIViewController currently implements its dealloc method using the setView: accessor method (rather than simply releasing the variable directly), self.anOutlet = nil will be called in dealloc as well as in response to a memory warning... This will lead to a crash in dealloc.
The remedy is to ensure that outlet variables are also set to nil in dealloc:
- (void)dealloc {
// release outlets and set variables to nil
[anOutlet release], anOutlet = nil;
[super dealloc];
}
I found what I was looking for in the Apple docs. In short you can set up your objects as properties that you release and retain (or just #property, #synthesize), but you don't have to for things like UILabels:
http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/chapter_3_section_4.html#//apple_ref/doc/uid/10000051i-CH4-SW18
The
[anOutlet release], anOutlet = nil;
Part is completely superfluous if you've written setView: correctly.
If you don’t release it on dealloc it will raise the memory footprint.
See more detail here with instrument ObjectAlloc graph
Related: Understanding reference counting with Cocoa / Objective C
You do alloc the label, in a sense, by creating it in IB.
What IB does, is look at your IBOutlets and how they are defined. If you have a class variable that IB is to assign a reference to some object, IB will send a retain message to that object for you.
If you are using properties, IB will make use of the property you have to set the value and not explicitly retain the value. Thus you would normally mark IBOutlet properties as retain:
#property (nonatomic, retain) UILabel *lblExample;
Thus in ether case (using properties or not) you should call release in your dealloc.
Any IBOutlet that is a subview of your Nib's main view does not need to be released, because they will be sent the autorelease message upon object creation. The only IBOutlet's you need to release in your dealloc are top level objects like controllers or other NSObject's. This is all mentioned in the Apple doc linked to above.
If you dont set the IBOutlet as a property but simply as a instance variable, you still must release it. This is because upon initWithNib, memory will be allocated for all IBOutlets. So this is one of the special cases you must release even though you haven't retained or alloc'd any memory in code.