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.
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.
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.
Should I implement dealloc in my app delegate and release my ivars there? As I understand it, when an app gets terminated, all the memory associated with it gets freed automatically. So basically, there's no need to release any ivars yourself at termination.
I've found this question here already: Does it make any sense to release ivars in appdelegate's dealloc?
One of the answers says that objects might have clean up code in dealloc, so you might want to release ivars yourself at termination. But when I put an NSLog in the dealloc of my app delegate, it's never called. My assumption is there's no use at all for it so I don't even have to implement it, am I right?
It may be necessary in future iOS releases. For the sake of forward compatibility, and since Apple seems to recommend it I would release those ivars.
You are right, you don't need to release your ivars in dealloc (The example templates that come with the SDK do have a -dealloc though). The OS will reclaim any memory associated with you app. If anything, it will just add a small amount of overhead. Also, as far as I know, there isn't any guarantee by the environment that the -dealloc in your app delegate will ever get called, so it may never even execute.
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.