Why assign nil to IBOutlets in viewDidUnload? - iphone

I have a UIViewController that has an IBOutlet for a UILabel with the label wired up in the XIB.
#import <Foundation/Foundation.h>
#interface MyViewController : UIViewController {
IBOutlet UILabel *aLabel;
}
#end
According to iOS Programming: The Big Nerd Ranch Guide (2nd Edition) chapter 7
When [MyViewController] reloads its view, a new UILabel instance is created from the XIB file.
Thus it recommends releasing the label in viewDidUnload.
- (void)viewDidUnload {
[super viewDidUnload];
[aLabel release];
aLabel = nil;
}
Being a C# programmer, I've had it drummed into me that assigning nil/null to things is pointless. While I can see it makes more sense in Objective-C, it still grates my sense of code aesthetic slightly*. I removed it and it all worked fine.
However, when I tried to do a similar thing with MKMapView the application errors EXC_BAD_ACCESS while trying to load the NIB.
#import <Foundation/Foundation.h>
#interface MyViewController : UIViewController {
IBOutlet MKMapView *mapView;
}
#end
- (void)viewDidUnload {
[super viewDidUnload];
[mapView release];
mapView = nil; // Without this line an exception is thrown.
}
Why is there an error when mapView is not set to nil, but not when aLabel is not set to nil?
* I realise I need to adjust my sense of code aesthetic for the new language, but it takes time.
It turns out I was just flat out wrong about aLabel not being referenced. Not sure what made me think it wasn't.
However, that still leaves the question of why they're being referenced while the NIB is being loaded.
When the field or property is set, a release message is sent to the old value (either the synthesized properties set method sends the release message, or setValue:forKey: sends the message if it's a field). Because the old value has already been released, this results in EXC_BAD_ACCESS.

It's because of memory management, specifically the lack of garbage collection.
In C# (as you know) objects that are no longer in scope are removed. In objective-c, that doesn't happen. You have to rely on retain/release to tell the object when you are done with it.
There's a drawback to the objective-c reference counting method that your mapView bug exhibits. Calling release on an object might result in it being deallocated. However, your pointer to the object will still point to the same place - your object just won't be there anymore.
For example
// We create an object.
MyObject *object = [[MyObject alloc] init];
// At this point, `object` points to the memory location of a MyObject instance
// (the one we created earlier). We can output that if we want :
NSLog(#"0x%08X", (int)myObject);
// You should see a number appear in the console - that's the memory address that
// myObject points to.
// It should look something like 0x8f3e4f04
// What happens if we release myObject?
[myObject release];
// Now, myObject no longer exists - it's been deallocated and it's memory has been
// marked as free
// The myObject pointer doesn't know that it's gone - see :
NSLog(#"0x%08X", (int)myObject);
// This outputs the same number as before. However, if we call a method on myObject
// it will crash :
NSLog(#"%#", myObject);
In objective-c, if you try to call a message on nil, nothing happens. So if each time you are finished with an object and call release on it, you should also set it to nil - this means that if you try to use that pointer again, it won't crash!

viewDidUnload is usually called when the device receives a memory warning.
In the View stack there may be situations where the device can free up memory by releasing objects that aren't in use. Imagine you have a navigation stack with a number of view controllers. The interface view controllers lower on the stack are not accessible but are still using up memory. So its usually a good idea to nil out any interface elements that can't be accessed. These will then be reloaded with viewDidLoad when needed.
Generally in ViewDidUnload you should release any view objects that are created from a Nib file or allocated in your ViewDidLoad method
Your mapView is throwing an exception because your view controller is trying to access to MapView but the mapView has been released. When you set the outlet to nil any messages sent to it are ignored.

Related

MKMapView initWithFrame not releasing memory with ARC

I've been using instruments to find allocations that aren't being released properly. I have MKMapViewDelegate that adds a map via a instance method after it has been instantiated. I can see in the call tree that this method keeps retaining about 300KB of memory, after the ViewDelegate is released. I commented out the meat of the code and it stills maintains the memory with just this line:
self.map = [[MKMapView alloc] initWithFrame:CGRectMake(10, 210, 300, 125)];
I look in the object list and the MKMapView itself isn't living, but as I keep creating new ViewDelegates, that memory keeps adding up. Here is how map is defined:
#property (strong, nonatomic) MKMapView *map;
The map's delegate is set to nil, as well as the reference on the ViewDelegate's dealloc
self.map.delegate = nil;
self.map = nil;
Once you set the delegate to nil, there are no longer any pointers, and iOS will release it when it wants to. iOS may not release the memory immediately after setting it to nil.
Remember, that you are removing the pointer by setting it to nil, but the object still remains on the heap, basically doing nothing, until something else gets allocated there.
(I'm assuming you also removed the MKMapView from its superview using [self.map removeFromSuperView]).

addSubview of UIViewController, view gets over released

EDIT:
Solved the problem myself. Turned out it was a leftover in the dealloc method that caused a UIButton to be released twice...
I'm trying to display a UIViewController on top of another UIViewController like a popup. Problem is that the view seems to be getting overreleased. With NSZombieEnabled, I get the following error:
[CALayer release]: message sent to deallocated instance 0x784bf40
I use this code to add the view:
//self.someViewController is declared as (nonatomic, retain)
self.someViewController = [[[SomeViewController alloc] initWithDelegate:self] autorelease];
[self.view addSubview:self.someViewController.view];
Then later on, I remove the view like this:
[self.someViewController.view removeFromSuperview];
self.someViewController = nil;
Should earlier comments not solve this perhaps this may help. I'm assumning you've created your someViewController property like this
#property (nonatomic, retain) NSViewController* someViewController;
in which case I believe your code is correct (at least I can see how it should work) and you might be seeing a secondary crash here.
I.e. when you're calling
self.someViewController = nil;
this should be freeing the memory up immediately (assuming a frame has gone by where the VC exists so the autoreleased count has already been decreased). Therefore if you have ANOTHER object being used in that someViewController VC who still exists and has a delegate set to your someViewController object and is doing a background task it will cause a crash when it trys to call back to your now deallocated object. (If you don't free your VC you wouldn't see this crash)
For example if you have a MKMapKit being displayed in someViewController and the delegate is set to someViewController... if you have implemented the method in someViewController
mapViewDidFinishLoadingMap:(MKMapView*)mapView
the MKMapKit may still be calling this from another thread if you haven't destroyed MKMapView object before yours.
I would always set other object delegates pointing to your VC (like MKMapView) to nil before destroying the said VC it uses to avoid this risk. For PDF rendering (CALayer?) you may find you need to explicitly release the object too given this uses a different memory alloc/free paradigm.
Solved the problem myself. Turned out it was a leftover in the dealloc method that caused a UIButton to be released twice...

Iphone: Where to release objects?

where i should release objects: in method dealloc or viewDidUnload?
Thanks
The correct way is to release them and set them to nil in both of those methods.
You need to release your objects in viewDidUnload since memory warnings can happen, and if your view doesn't have a superview then you should release your outlets to save memory. The framework will issue a viewDidLoad again if the view was unloaded.
You need to release your objects in dealloc, since viewDidLoad + viewDidUnload is not necessarily called.
Finally you need to set your variables to nil in both of the methods, to not be able to call release on them the second time.
A short answer for you question: dealloc()
A long and more complicated answer for your question: both
release any unused IBOutlets in viewDidUnload(). This method will be called when your device is running out of memory.
release any objects that the current view controller is responsible for memory management, and release them in dealloc(). (An autoreleased object doesn't belong to this category)
Any objects allocated and/or retained as part of loadView and/or viewDidLoad should be released in viewDidUnload. Releasing anything you alloc in viewDidLoad is easy to grasp, loadView is a bit harder if you are using a NIB. Any IBOutlet that is a property defined as retain will be implicitly retained as part of loadView.
If the view have for example a subview that is a UITextField and you connect this view to a property defined as:
#property(nonatomic, retain) IBOutlet UITextField* nameField;
Then the actual text field when loaded from the NIB will have a retain count of +2. +1 because of it's parent view, and +1 because of the property you connected it too. Thus it's memory is not freed until the view controller is released, or the NIB is loaded again.
Unfortunately viewDidUnload is not called when a view controller is deallocated. So you must explicitly release all your `IBOutlets here as well. I use this patter in order to not forget to release anything:
-(void)releaseOutlets {
// Set all outlets to nil
}
-(void)viewDidUnload {
[self releaseOutlets];
[super viewDidUnload];
}
-(void)dealloc {
[self releaseOutlets];
// Release anything else.
[super dealloc];
}
dealloc this way if the parent object is released the child objects will be released as well.

Objective-C: Point of releasing a View

I made a static mainmenu with a TableView. Sometimes it crashes because my Subview has allready deallocated a subview.
Is also ok to release a View in the dealloc method of the local object instead of that:
[NavController pushViewController:self.AnotherView animated:YES];
[self.AnotherView release]; //This line into (void)viewDidLoad
AnotherView is defined in the headerfile as property and also synchronozed in the .m-File
When i use the dealloc way it works great on the device, but I need to know if this is correct.
You only call release for objects you init or alloc yourself. If it is a property of your class then release in the dealloc of your class.
So in your case, unless you init anotherView a few lines above your sample code (same method), calling release on it where you are is going to cause a leak/SIG_ABORT because you have done so prematurely.
Feel free to post more code, particularly how anotherView is assigned and you may get a more specific answer.

Do I need to release xib resources?

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.