I'm currently working on fixing some iOS 7 display issues on a legacy app which was not built with ARC, and have run into some inconsistent behaviour with the dealloc method between iOS versions 6 & 7. I can't find any other mention of these changes in any documentation or community discussions, so I wonder if anyone here could shed some light on what's happening here?
My previous code, (which works in iOS6, looks like this):
#interface MyViewController()
#property (retain) AdHandler *adHandler;
#end
#implementation MyViewController
#synthesize adHandler = _adHandler;
- (id) initWithAdHandler:(AdHandler*)anAdHandler
{
self = [super init];
_adHandler = [anAdHandler retain];
return self;
}
- (void)dealloc
{
[super dealloc];
[_adHandler release];
_adHandler = nil;
}
...
#end
When stepping through the code in iOS 6, I've found that after the dealloc statement, [_adHandler retainCount] is still positive, and the object is still available.
In iOS 7 however, after the dealloc statement, retainCount has somehow hit zero, and the _adHandler object has been dealloc'd, and therefore my call to release causes an EXC_BAD_ACCESS.
I can fix this simply by moving my [adHandler release] call to before the dealloc call, but my question is why is this happening? Why is dealloc releasing objects that it has no responsibility for? Is there any documentation anywhere on why dealloc behaviour has changed in this way?
After [super dealloc] the instance is garbage and whatever happens is rather random and non-deterministic. As #bneely wrote, [super dealloc] must be last.
Best practice: convert to ARC.
As for retainCount, there are no guarantees what it's value may be, don't use it, it just caused confusion. In your case you destroyed the class instance by calling [super dealloc] and then expect the instance to behave as if it still exists. It can't, it has been destroyed and is now just some non-deterministic bits in memory.
You should never use retain count.
The values it returns cannot be interpreted by you in any reasonable way. Thus it's entirely expected to see different results on different iOS versions, devices, with different code bitness, etc.
On a side note, do you consider switching to ARC? The code would greatly simplify. Also note that if you're implementing a subclass of UIViewController, you shouldn't initialize it with init. Rather just declare the property and ARC will take care of its setters and getters:
#interface MyViewController : UIViewController
#property AdHandler *adHandler;
#end
// somewhere else
MyViewController * mvc = ... from nib or in some other way ...
mvc.adHandler = myAdHandler;
Now you're guaranteed no bad accesses.
Related
I am currently fixing memory leaks in our SDK. I am getting memory leaks mostly in strong references and class methods. Due to my lack of understanding in memory management, I find it hard to fix those leaks. I already read a lot about memory management but still I was not able to determine where the problems are located. So, I figured that I should know first how does balancing retain/release works just by looking at the code. As a result I switched off ARC did a little test.
This is my source code:
ViewController.h
#interface ViewController : UIViewController
#property (nonatomic, assign) IBOutlet UILabel *lLineOne;
#property (nonatomic, assign) IBOutlet UILabel *lLineTwo;
#end
ViewController.m
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *test = [[NSString alloc] initWithFormat:#"%# %d", #"Test", 1];
_lLineOne.text = test;
_lLineOne.text = nil;
}
#end
I pretty much know that this will definitely cause a leak, because I didn't get to release test.
Then I use the instruments(leaks) to check the retain/release list. And here is what I got from the history of that string
I know that event#0 which is the malloc will increment the retain count to 1, then followed by setting that NSString to the property of UILabel will also increment the retain count by 1, so that gives us a 2. I guess the CFRetain is something that happens at a lower level in the code of NSString allocInitWithFormat but I am not sure why it contributes to another +1. Followed by that, what is that Retain in event#3? It seems that it is caused by UILabel, but I really don't have any idea about what it really is.
First the standard warning: trying to track the retain count is not recommended, for the simple reason it is not in general possible for you to know what it should be.
You are seeking an answer you don't need. Most of the time all you really need to know under manual memory is if you create or retain you are responsible to arrange the release.
In your example you create the string, so you must arrange it's release after you no longer require it. UILabel may copy or retain at its choice, you don't need to know - you have to trust it follows the rules.
The "most of the time" above is largely to do with delegates. It is common for objects passed as delegates not to be retained by the callee, leaving the caller to keep the object around as long as the callee needs it.
HTH
I have the problem in an iOS app that after some time, an object isn't dealloc'ed as it should be. I suspect that this is because there is still a reference to it. I'm using ARC.
I want to find out where that reference is created. I will then be able to tell where it should be NULLed or if it should be made a weak reference.
What I imagine as a possible solution:
If I could set a breakpoint for every place where the reference count, aka retain count, is modified, then I will be quick to find the problem. I just don't know how to set such a breakpoint. Maybe at pre-ARC times, this could have been done by setting breakpoints inside retain and release, but I have no idea how to do this with ARC.
Highly simplified example code:
I've done this in one of my classes and I know where:
// ShouldBeDeallocated.m
- (void) dealloc {
NSLog(#"%s", __FUNCTION__); // this never shows up in output
}
And I suspect that I wrote code like this some time ago, but I can't find where that was:
// UnknownSuspect.m
#interface UnknownSuspect ()
#property (strong, readwrite) id referenceWhichIsNeverNeeded;
#end
- (void) someMethod:(ShouldBeDeallocated*)ref {
self.referenceWhichIsNeverNeeded = ref;
// The object pointed to by referenceWhichIsNeverNeeded will
// not be dealloc'ed unless the property gets overwritten.
}
You can use Instruments to profile memory allocations. Then you can see where in your code it is allocating it and changing the retain count. Instruments can also help track down where you have memory leaks in your code, which is still possible even with ARC.
I'm pretty sure I somehow made a stupid mistake but I seem to be unable to fix it. I got a subclass of CCScene, which in turn has a subclass of cclayer as a layer, roughly looking like this:
#interface MyLayer : CCLayer {
}
// some methods
#end
#interface MyScene : CCScene {
MyLayer *_myLayer;
}
// some methods
#end
On constructing the I do the following:
-(id) init {
if (self = [super init]) {
_myLayer = [MyLayer node];
[self addChild:_myLayer];
// more stuff
}
}
I need the reference in _myLayer because I need to interact with the layer. However, this leaves me with a retain count of 2 (once in _myLayer, once as a child node of the scene). No problem so far - at least as I'm understanding it. This, however, also means, I have to release it. So, the dealloc of MyScene looks like this:
-(void) dealloc {
[_myLayer release];
_myLayer = nil;
[super dealloc];
}
If I now release the Scene during runtime, everything works fine. I folled the process of releases and it's all good, I can release the whole scene including the layer without problems. MyScene::release is called once, lowers the retain count by one when actively calling [_myLayer release], MyLayer::release is called once (through the [super dealloc] - delete all children in CCNode), everyone is happy.
However, as soon as I quite the whole game (kill the app on the device) and CCDirector::end is called, the whole thing breaks, because in fact, it tries to release the _myLayer twice - once with the explicit call, once through releasing the children.
Now I could understand that I made some kind of mistake if it would be the same in both cases - but it isn't. Once it works as expected - in the first case lowering the retain count by one, then again, releasing it, once it works differently. And I have no clue why that is the case.
I tried scrapping the [_myLayer release] alltogether. In that case _myLayer doesn't get released at all during runtime but everything works out fine during shutdown. So it's kinda consistent here, but that doesn't really help me.
First of all: retainCount is useless.
This here returns an autoreleased instance of MyLayer:
_myLayer = [MyLayer node];
It is the equivalent of:
_myLayer = [[[MyLayer alloc] init] autorelease];
If you were to leave it at that with no other code, the _myLayer would become a dangling pointer some time after the init method returns. Definitely the next time the autorelease pool is purged, which if I remember correctly happens ever frame in cocos2d. Under ARC, the ivar by itself defaults to being a strong reference, so the layer would be retained and would not deallocate as you would expect. So if you don't like autorelease and how it behaves, use ARC. ;)
Then you add the layer as child, which puts it in an array, which means this retains the layer:
[self addChild:_myLayer];
So as long as the layer remains a child of the scene, it will not deallocate.
And now, like so many before you, you were looking at the wrong place to fix the problem of the layer not releasing. By doing this in dealloc you add an extraneous release:
[_myLayer release];
Now this works fine for the moment because the actual problem is the layer not releasing, and you force it to be released here. However some time later the scene's children array will be released, which sends release to each object, which then causes the crash due to over-releasing the layer.
Hence the actual problem that you should be tracking down is why the layer doesn't deallocate. And here I sense more problems:
I can release the whole scene
If by that you mean you were sending the release message to the scene, then that's wrong. And again this would over-release the scene. Cocos2d will clean out the scene when you call replaceScene or similar methods. The scene itself is typically autoreleased as well, definitely when created through the node or scene class methods.
If that's not what you're doing and the layer doesn't release, then check if maybe you have a retain cycle. Or perhaps you're simply expecting the layers to deallocate before the scene? That doesn't necessarily have to be in this order, with autorelease no less.
You can easily create a retain cycle by having two (or more) sibling nodes holding on to each other (ie layer A retains layer B and layer B retains layer A) or by a sibling retaining its parent, for example _myLayer holding a retained reference to the scene (which btw is the same as accessing it via self.parent).
Now I'm saying to use ARC because it makes all those problems go away almost instantly, except for retain cycles. But for retain cycles it provides a very simple and effective cure: zeroing weak references.
For example you could declare the layer reference as weak in the interface and no longer worry about it retaining the layer:
__weak MyLayer *_myLayer;
Moreover when the layer is released, _myLayer will automatically be set to nil. And this happens the instance the layer has no more strong references, not at some later time as is the case with autorelease.
The positive side effect is that you can now safely do the following:
#interface LayerA : CCLayer
#property (weak) LayerB* layerB;
#end
#interface LayerB : CCLayer
#property (weak) LayerA* layerA;
#end
Under MRC this would create a retain cycle if you assign the layers accordingly and don't set them to nil before the dealloc method. The tricky thing about retain cycles is that they can not be resolved within the dealloc method since by definition all objects participating in a retain cycle won't be deallocating. A typical place to do so in cocos2d is the cleanup method.
But now with ARC there is absolutely no problem. Either or both layers will deallocate because the reference in the other layer is not retaining (weak) and when either of the layers is deallocated the reference is set to nil, so there won't be any crashes.
I have been developing exclusively with ARC for two years. I never looked back. My quota of errors relating to memory management went down to almost zero, it's ridiculous. About the only thing I occasionally have to look into is when a weak reference is nil when I don't expect it to be. Usually that's when I incorrectly assume the object has a strong reference somewhere, but doesn't.
Using ARC is such a huge timesaver that even if you really really really really absolutely badly want to learn this, you better be really really really really almost exclusively interested in how memory management used to work back in the old days of Objective-C development. You know, like when my grandma was still writing code for the very first iPhone! :)
PS: it's a myth that you give up control over memory management when using ARC. There's a lot of neat tricks you can do to gain control back, even for a short time. Mainly by using bridge casting. So even if ARC can take a couple more cycles and that may add up in a tight loop, you can still hand-optimize the code (if you have to; you'll probably never ever have to) with MRC. This is beyond the scope of this answer (I already went too far) but these options do exist, but one hardly ever needs to exercise them.
This is why I'm brash about using ARC because not doing so is borderline irresponsible. IMO.
In dealloc, dont release myLayer, it already being held for you (when you addChild). Instead, i tend to 'removeAllChildrenWithCleanup:YES', this will release myLayer. What happens here (i suspect) is that on end, the director is trying to do just what i said BUT you already released myLayer. So it gets a zombie in its array of children.
Should the Variables with the #property nonatomic and retain be released explicitly.
#interface MyScreenViewController : UIViewController <UIWebViewDelegate> {
UIWebView* greetingView;
}
#property(nonatomic, retain) IBOutlet UIWebView* greetingView;
- (void)dealloc {
[greetingView release];
}
Is release in delloc method Required ???
Ofcourse. YES you should release your properties with retain,copy and don't for assign. You set the property to Nil in viewDidUnload.
- (void)viewDidUnload {
[super viewDidUnload];
self.greetingView = nil;
}
- (void)dealloc {
[greetingView release],greetingView = nil;
[super dealloc];
}
Refer memory management in UIViewController Class reference: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html
Memory is a critical resource in iOS, and view controllers provide built-in support for reducing their memory footprint at critical times. The UIViewController class provides some automatic handling of low-memory conditions through its didReceiveMemoryWarning method, which releases unneeded memory. Prior to iOS 3.0, this method was the only way to release additional memory associated with your custom view controller class but in iOS 3.0 and later, the viewDidUnload method may be a more appropriate place for most needs.
When a low-memory warning occurs, the UIViewController class purges its views if it knows it can reload or recreate them again later. If this happens, it also calls the viewDidUnload method to give your code a chance to relinquish ownership of any objects that are associated with your view hierarchy, including objects loaded with the nib file, objects created in your viewDidLoad method, and objects created lazily at runtime and added to the view hierarchy. Typically, if your view controller contains outlets (properties or raw variables that contain the IBOutlet keyword), you should use the viewDidUnload method to relinquish ownership of those outlets or any other view-related data that you no longer need.
Yes, you should release it, because it is a retained property. Anything property that is retained (or copied) should be released by the same class (if it is assigned, it doesn't need to be released).
Retain is basically like a class saying, "I am going to be using this other class, so keep it around in memory." dealloc is where the class that said that is, itself, going to be removed from memory. So if the class that needed your retained property is about to go away, that object itself should be deallocated, too.
Otherwise it is just going to sit there in memory, when nothing else in the program needs it. And if that kind of thing keeps happening, you will run out of memory.
(Also, make sure you that you call [super dealloc] at the end of of your dealloc method.)
I have a View Controller as part of a navigation controller stack with two IBOutlets. In viewDidUnload I free them up:
- (void)viewDidUnload
{
self.myView1 = nil;
self.myView2 = nil;
[super viewDidUnload];
}
But I still had a leak. So I stuck release messages in the dealloc for them too:
- (void)dealloc
{
[myView1 release];
[myView2 release];
[super dealloc];
}
This appears to clear the memory leak. However, I was always told that I should only release ivars that I have created using alloc, copy or new. So I'm worried about these two releases being in here. Is this right or wrong? Can someone please explain this to me because I keep getting conflicting opinions... Thanks!
If any of your #property objects are declared retain or copy, you need to release them in dealloc. This includes your outlets.
By using IBOutlet, the variables are exposed to be connected in Interface Builder and allocated when the view controller is initialized. So they have to be released and deallocated, as the view controller is unloaded and deallocated. Since most IBOutlets are retained UI* properties, this is necessary.
Assigning nils to the variables is technically not deallocating. It's simply the last state to have retain count 0, just before actually being deallocated.
Also, please note they are referenced using self. It means, the references from the view controller becomes nil, not the allocations.
So in conclusion, IBOutlet properties must be released in dealloc()
(Though I am quite confident, someone else may provide 100% correct answer for this.)
The basic, safe pattern is
declare ivar
declare IBOutlet property for ivar
release property in dealloc
only reference property, never ivar
The xib sets the property, which release whatever might have been there first.
I'm a bit confused why that leak was there though. Setting the property to nil should release the old reference. Perhaps viewDidUnload wasn't even getting called? Are you sure you even need viewDidUnload?