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]).
Related
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.
I am having an iphone app with admob on two screens's viewdidLoad
My code is:
AbMob =[[GADBannerView alloc]initWithFrame:CGRectMake(0.0,self.view.frame.size.height-195, 320, 50)];
AbMob.adUnitID = AdMob_ID;
AbMob.rootViewController = self;
[self.view addSubview:AbMob];
GADRequest *r = [[GADRequest alloc] init];
r.testing = NO;
[AbMob loadRequest:r];
Problem is this code works fine on one screen but crashes on other screen with error
* -[GADOpener didOpen]: message sent to deallocated instance
0x6074750
Can anybody tell me what could be the problem
You have a retain/release problem somewhere in your code. You say it works in one view, but not another - this makes me believe that you are storing this instance outside of your view controllers. The message sent to deallocated instance issue is due to you trying to use a variable that has been removed from memory somewhere in the code before this error pops up. You need to ensure that the view controller that is creating this object is properly retaining it so that it does not get deallocated before you need to use it again with:
GADBannerView *_adMobBannerView;
#property(nonatomic,retain) GADBannerView *adMobBannerView; //view controller retains object when using self.adMobBannerView = bla
It sounds like you may need to brush up on your memory management documentation, but the gist of it is that anywhere you are calling alloc, you are managing that memory and need to call release when you are done with it. If you need a variable to stick around for longer than an autoreleased object is living for, then you need to create an instance variable and retain the object yourself with ivar properties #property (nonatomic, retain).
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...
Here's a common practice I see often (including from a very popular iPhone developer book)
In the .h file:
#interface SomeViewController : UIViewController
{
UIImageView *imgView;
}
Somewhere in the .m file:
imgView = [[UIImageView alloc] initWithFrame:[[UIScreen mainScreen]
applicationFrame]];
[imgView setImage:[UIImage imageNamed:#"someimage.png"]];
[self addSubview:imgView];
[imgView release];
And later, we see this...
- (void) dealloc
{
[imgView release];
[super dealloc];
}
Since imgView has a matching alloc and release, is the release of imgView in dealloc necessary?
Where is the imgView retained by the addSubview call accounted for?
The code is incorrect. You'll end up releasing imgView after it's been deallocated.
In your .m file, you:
alloc it --> you own it
add it as a subview --> you and the UIView owns it
release it --> you don't own it
Then in dealloc, you release imgView even though, as we established at step 3 above, you don't own it. When you call [super dealloc], the view will release all of its subviews, and I imagine you'll get an exception.
If you want to keep an ivar of imgView, I suggest not calling release after you add it as a subview, and keep your dealloc the same. That way, even if imgView is at some point removed from the view hierarchy, you'll still have a valid reference to it.
The code is incorrect, you shouldn't be releasing it in the init method, just when dealloc is called (that's if you want to keep it as an ivar, you don't need to unless you need a pointer to it elsewhere since addSubview: will retain the view for you).
I believe the reason it's not actually crashing is because it's still being retained by the superclass (from the call to addSubview:), so when it's released in dealloc that's actually balanced out. The view probably removes itself from the superview when it's deallocated immediately afterwards, so when [super dealloc] is called it's not being over-released. That's my hunch, at lease.
Release in init is incorrect.
You mentioned "common practice" and an un-named book. I suggest looking at the canonical examples from Apple: ViewTransitions is a good example for this case (and 2 views to boot ;)
http://developer.apple.com/iphone/library/samplecode/ViewTransitions/index.html
(I don't have enough reputation to add comment yet.)
#bentford: Correct me if I'm wrong, but I believe that in order to use the imgView property's synthesized setter, you must use "self.imgView":
self.imgView = [[UIImageView alloc] initWithFrame:[[UIScreen mainScreen]
If you don't have self., it's just using the ivar, and it's not getting the additional retain.
The basic answer is, there should only be one [imgView release] in the example code (whether it's after addSubview or in dealloc). However, I would remove [imgView release] from dealloc and leave it after addSubview.
There is a catch on the iPhone; with didReceiveMemoryWarning, you could have objects (including an entire view) released out from under you. If you have an application-wide retain set and you don't respect memory then you could find the application simply being killed.
A good example is:
if you think of a nested set of 3 views, View 1-> View 2-> View 3.
Next, consider the 'viewDidLoad' and 'viewDidUnload' calls. If the user is currently in 'View 3', it's possible that View1 is unloaded, and this is where it gets nasty.
If you allocated an object inside viewDidLoad and didn't release it after adding it to the subview, then your object isn't released when view1 is unloaded, but, view1 is still unloaded.
viewDidLoad will run again and your code will run again, but now you've got two instantiations of your object instead of one; one object will be in nowhereland with the previously-unloaded view and the new object will be for the currently visible view. Rinse, lather, and repeat and you find your application crashing from memory leaks.
In this example, if the given block of code is volatile and has a chance to be executed again (whether because of memory or an unloaded view), I would remove [imgView release]; from dealloc and leave it after addSubView.
Here is a link on basic retain/release concepts:
http://www.otierney.net/objective-c.html#retain
Yes, that code has problems. It releases the imgView too early which could potentially cause crashes in rare circumstances stores an object in an instance variable without retaining it, and it's just generally going about memory management the wrong way.
One correct way to do this would be:
#interface SomeViewController : UIViewController
{
UIImageView *imgView;
}
#property (nonatomic, retain) UIImageView *imgView;
And in the implementation;
#synthesize imgView;
Somewhere in the module:
//Create a new image view object and store it in a local variable (retain count 1)
UIImageView *newImgView = [[UIImageView alloc] initWithFrame:self.view.bounds];
newImgView.image = [UIImage imageNamed:#"someimage.png"];
//Use our property to store our new image view as an instance variable,
//if an old value of imgView exists, it will be released by generated method,
//and our newImgView gets retained (retain count 2)
self.imgView = newImgView;
//Release local variable, since the new UIImageView is safely stored in the
//imgView instance variable. (retain count 1)
[newImgView release];
//Add the new imgView to main view, it's retain count will be incremented,
//and the UIImageView will remain in memory until it is released by both the
//main view and this controller. (retain count 2)
[self.view addSubview:self.imgView];
And the dealloc remains the same:
- (void) dealloc
{
[imgView release];
[super dealloc];
}
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.