Under ARC, what's the point of making every IBOutlet a property? What would be the downside of using ivars for IBOutlets used only internally by the view controller?
If you don't use the setter/getter methods for anything, don't rely on key-value observing for those properties, and don't anticipate that a subclass would benefit from overriding those properties, then there's no real downside to just using ivars for IBOutlets under ARC.
I've been using ivars for my "private" IBOutlets and ran into memory leak problems. I think it's because my IBOutlets used the __unsafe_unretained attribute instead of __weak. I can't use __weak because it's not supported on iOS 4 (I want my app to be backwards compatible with iOS 4). It's difficult to grasp what's really happening with ARC, IBOutlets, viewDidUnload, and all that mess. Sigh...
Anyways, when I changed my IBOutlets from ivars to properties, the memory leak issues went away.
So, to answer my own question, one downside of using ivars for IBOutlets is that you might run into memory leaks if you have the __unsafe_unretained attribute.
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.
I understand the ARC Release Notes, however I have been wondering what does this mean exactly and what are the system classes:
You may implement a dealloc method if you need to manage resources other than releasing instance variables. You do not have to (indeed you cannot) release instance variables, but you may need to invoke [systemClassInstance setDelegate:nil] on system classes and other code that isn’t compiled using ARC.
This is right here ARC Release Notes under the new rules enforced by ARC
What are the so called system classes here?.
I take this to mean any class starting with 'NS' or 'UI'. Apple have not rebuilt all the frameworks from the ground up to use ARC. Instead, your new ARC code should happily interoperate with the existing frameworks if you follow the rules.
In particular, delegate properties of system classes (such as UIApplication) are still declared as (nonatomic, assign) instead of (nonatomic, weak). This means that these properties do not automatically zero themselves when the delegate is deallocated. In fact, assign is a synonym for unsafe_unretained under ARC. Hence the advice to manually nil the delegate property in your dealloc method. This is so that there is no chance that the system class will attempt to access its delegate after it has disappeared.
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).
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.)
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.