Strange ARC behaviour in non-optimised vs optimized binary - ios

I had a hard to track bug which only appeared in the Release build of my app, but not in the Debug build. The relevant difference between the builds turned out to be that the Debug build was compiled without any compiler optimization, whereas the Release build was compiled with -O (the bug was reproducible on all other optimization settings as well). This is all on LLVM.
In my view controller I have a property self.basicInfoContainerView defined as:
#property (weak, nonatomic) IBOutlet UIView *basicInfoContainerView;
I then removed the subview from one view, and added it onto another.
[self.basicInfoContainerView removeFromSuperview];
[self.infoTextView addSubview:self.basicInfoContainerView];
Depending on the compiler optimization level, different things happened.
With optimization on: as soon as the view was removed from its superview, the view was deallocated and self.basicInfoContainerView was a zero'ed, and as a result was not added as a subview to the new view.
With optimization off: the subview was not immediately deallocated and was successfully added as a subview to the new view.
(When I changed the property storage qualifier to strong, the view survived in both cases, but even though that solved the problem, but that's not really my question.)
I would love someone to help me understand what is really going on here. Why does weak not immediately release my view (and zero the pointer if retain count == 0) when compiler optimization is turned off?

It's really not unusual to see non-optimized code that keeps extra local (and strong) references to objects. So your unoptimized code must have a local strong reference to this 'basicInfoContainerView'. That reference stays in scope through the method, and isn't being released probably until the method returns.
This is really just an accident of sorts that is masking a real bug in your code. The fact of the matter is as soon as you do [self.basicInfoContainerView removeFromSuperview], you can't expect that your basicInfoContainerView will survive, because you don't have any explicit strong reference to that view any more.
The way to fix this, of course, is to create an explicit strong reference to that view. Then you've made your intention clear to the compiler, and you should get the result you want whether the code is optimized or not:
UIView *containerView = self.basicInfoContainerView
// this local variable is that explicit strong reference you need
[containerView removeFromSuperview];
[self.infoTextView addSubview:containerView];
// the compiler can/will release this view when the local variable
// 'containerView' goes out of scope
Hope that helps.

Related

Cocos-2d iOs - different memory release behaviour of cclayer

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.

Disadvantage of using strong/retained for IBOutlet?

I have read several Q&A and documentations which state that we should use weak for IBOutlet unless it's top level objects from File's Owner.
But if I still use strong/retained, is there any major downside, or is it just redundant because the subview is already retained with addSubview:?
Note: please do not copy definition of weak / strong here, I don't need that, I want to see real world cases where using strong for IBOutlet could cause problems. Thanks.
With MRC, if you use retain, you will have to release the memory by yourself.
With ARC, if you use strong and the system requests memory from your app (= your view will be unloaded), you will have to release the memory by yourself (note that the controller would be still be active, so no dealloc called there)
For most outlets, weak/assign is appropiate because you don't need to care about releasing the memory.
Exceptions:
IBOutletCollection must be strong/retain. The collection (NSArray) is not retained by the view hierarchy.
You add/remove views dynamically. If you want to remove a view from your view hierarchy and use it again later, the view must be retained somewhere, otherwise it gets deallocated at the time of removal. However, note that you can always retain it in code at the time of removal.
I will mark this as "accepted" until someone provides a better answer.
Apparently the only downside is that when your view receives a memory warning, it would unload the view, and optimally all the subviews should be released. But since your controller still retains them if you use strong, you will have to nil them out manually in viewDidUnload.
From iOS 6, view is not unloaded upon receiving memory warning, so this becomes inconsequential. From a practical point of view there is no major difference between using weak or strong for IBOutlet afaik, unless you have to unload your view manually in your application.

Why not just declare all #properties as strong and nil them?

An application that I am working on, that uses ARC and needs to support iOS 4.3 and iOS 5, declares every outlet as #property (strong, nonatomic) IBOutlet in the .h file.
e.g.
// myClass.h
#property (strong, nonatomic) IBOutlet UITextView *myTextview;
I know that with ARC only properties which do not hold a strong reference to an object are released.
As a result, the App relies on - (void)viewDidUnload to set the property myTextview to nil.
i.e.
// myClass.m
- (void)viewDidUnload
{
[super viewDidUnload];
self.myTextview = nil;
}
I know, from Apple's Documentation, that Outlets should generally be weak except those from File's Owner ( i.e. A Runtime Object that owns the contents of the iOS Storyboard scene) to Top-Level Objects (my rule of thumb is to use anything that appears in the window with File's Owner, First Responder and View).
Anything I add to the view will be a subview and thus is retained by it's direct superview, meaning a weak reference should be used.
I am also aware that - (void)viewDidUnload is deprecated in iOS 6 and is not called.
1st Question : What are the issues with taking the approach of declaring every outlet as a strong property and setting it to nil in viewDidUnload, apart from the fact that viewDidUnload is deprecated in iOS 6?
My intuition tells me that it is because situations arise where you can set a pointer to nil, before viewDidUnload is called. So you should, to free up memory on the heap. Is there a noticable performance change if this is the case?
2nd Question : Should I go back throughout the project and change strong to weak? Why? Is it worth the time?
3rd Question : If I was to declare the property in a class extension, to 'hide' it, how does this affect my rule of thumb for deciding on when to use strong or weak.
I know there are many threads here that discuss this issue. But many I've found are out of date, and do not address this issue directly. Thanks.
First, a few of your presumptions need addressing:
I know that ARC only releases properties which do not hold a strong
reference to an object. As a result, the App relies on -
(void)viewDidUnload to set the property myTextview to nil.
Not quite. ARC never retained weak properties in the first place. As for strong properties, ARC still releases them, but not until dealloc is called.
viewDidUnload was never used to prevent leaks. It was essentially an optimization, and one that Apple decided was no longer worth the trouble. To understand, consider the standard lifecycle of a pre-iOS6 view controller:
1. Allocated
2a. View Loaded
2b. View Unloaded
3. Deallocated
Where 2a and 2b could be repeated any number of times. For example, a view controller at the bottom of a navigation stack (its view being hidden) could have its view unloaded in low memory situations. It would then be reloaded the next its view became visible.
The method was essentially saying "Hey view controller programmer, we're running low on memory, and nobody can see me anyways, so I'm releasing my view. If you could do the same for your strong properties, that would be great."
That process had subtleties and was generally confusing. As a result of the tediousness, Apple deprecated it. Views are no longer unloaded, so there's no point in implementing it. The key point is that all your strong properties will still be released in ARC's dealloc method.
I know that Outlets should generally be weak...
Why do you know that? There's nothing special about outlets. The 'IBOutlet' keyword is really just for Xcode's benefit when designing things with its visual tools. It has no effect on the compiled code. So, when thinking about strong vs weak outlets, use the same considerations that you do for any other property, namely "do I need this to exists, or am I okay with it disappearing on me?".
What are the issues with taking the approach of
declaring every outlet as a strong property and setting it to nil in
viewDidUnload, apart from the fact that viewDidUnload is deprecated in
iOS 6?
There are no issues with that. If you want your properties to exists as long as your controller, then use strong. viewDidUnload has nothing to do with this. On older iOS versions, you could release strong outlets in viewDidUnload if you want.
Should I go back throughout the project and change
strong to weak? Why? Is it worth the time?
Again, just use whichever qualifier makes sense for your use case. You're almost always safe using strong for you outlets.
If I was to declare the property in a class extension, to 'hide' it,
how does this affect my rule of thumb for deciding on when to use
strong or weak.
There's no difference.
1st question: The outlets are subviews of the main view which is a property of the view controller. If you declare them as weak, they have a retain count of 1, since they are subviews. If the view is released, they are also released. If you declare them as strong, they have a retain count of 2, since they are subviews, and strong properties of the view controller. So they are only released when the view controller is released (which releases also its view and its subviews). To my understanding, the difference is that when the view of a view controller is released, e.g. when it is not presented and memory is low, the outlets still allocate memory when they have been declared as strong.
2nd question: I believe in most circumstances it does not matter.
3rd question: I believe if you declare properties in a class extension, the simply belong to the class, and thus there is no difference in handling them compared to "real" class properties.
My approach would be to make sure not to declare properties as strong that could result in retain cycles, what happens, i.e., if you declare a delegate as strong.

Why set retain on #property for subviews?

Fairly straightforward question in two parts.
If a view retains its subviews, and we create a view hierarchy in Interface Builder where views are nested within others, why does the IBOutlet property for the nested subviews need to be set to retain? Wouldn't assign be an acceptable parameter for those subview properties?
I have a UIView subclass which adds a few subviews to itself upon initialization. To capture references to specific subviews, #property (nonatomic, assign) will suffice for that need, correct? For example, the main UIView adds a player score subview, then later wants to talk to that player score to update it. That reference only needs to be assigned, as the view proper is automatically retain by the UIView class, right?
1) It doesn't need to be. assign is fine. What made you think that you have to use retain?
2) Yes
By the way, are you using ARC? If so, use weak instead of assign (please don't ask why, it is well explained in every corner of stack overflow and the Internet in general).
Yes, it is true that in your case the subview will be retained by the view, so we don't technically need to retain it again. However, that is kind of fragile. What if in the future you add some code that removes that subview from its superview? Then you have a dangling pointer unless you make sure to nil it out.
It is general convention to retain instance variables, unless it is necessary not to (e.g. for delegates). If we go down the path of saying "oh we don't need to retain this instance variable because it's retained here; oh we do need to retain this other one because it's not retained; etc.", then we end up with very haphazard memory management, where every time we add an instance variable, we have to go and think about whether it is retained by something else or not; and then every time we use it, we have to remember whether we decided to retain it or not. It is precisely the kind of memory management nightmare that the memory management rules are designed to avoid.
And retaining the instance variable, what harm does it do? In this case, it just causes an additional retain and release when we assign it. Not a big deal, for the benefit of simplicity and consistency.

So what's the deal with ARC and releasing properties/subviews on viewDidUnload

I'm still learning iOS development and have been working with various tutorials and books. Some pre-ARC, some with ARC.
In some cases, we're taught to release all of the properties and subviews of a ViewController on viewDidUnload but in some cases I've been told this is no longer required.
Can someone give a definitive answer? In iOS 5+, does one have to do the whole:
-(void)viewDidUnload
{
[super viewDidUnload];
self.photoViewCell = nil;
self.photoImageView = nil;
self.firstNameTextField = nil;
self.lastNameTextField = nil;
}
... or not? If so, is this only for properties that are descendants of UIView or is it for all properties of the ViewController?
Thanks
So each view has a number of owners. When that "owner count" (usually referred to as the retainCount) reaches 0, that object gets destroyed.
In iOS 5, we now have weak references, which essentially means "don't own this object".
Before iOS 5, in our header files, you'd see
IBOutlet UILabel *myLabel;
And this label was added to the XIB file's view. myLabel has 2 owners in this case: it's superview (the view in the XIB file) and the view controller (by having the IBOutlet). When viewDidUnload get's called, the view controller's view has been released, and therefore it's ownership of myLabel is gone. So myLabel at this point only has 1 owner, the view controller. So we needed to release it in viewDidLoad to make sure it didn't have any owners and so was destroyed.
With iOS 5, you will often seen this instead
__weak IBOutlet UILabel *myLabel
This is saying that we don't want the view controller to be an owner of myLabel. So the only owner is the view controller's view. So when viewDidUnload get's called, the view controller's view has already been released, and so its ownership of myLabel has also been released. In this case, myLabel now has no owners and its memory is released. There is no need for the self.myLabel = nil; there.
So with iOS 5, the recommendation is to make all of your IBOutlets a weak reference. With this, you don't even need to implement viewDidUnload, as all the memory has been taken care of for you.
But even if you are using iOS 5, if your IBOutlets aren't weak references, you'll need that code in viewDidUnload.
viewDidUnload has nothing to do with retain counting, automatic or otherwise. This method will be called when a view is unloaded due to memory pressure, which means that you should also nil (and release, non-ARC) elements of the view that you hold strong references to. Failure to do this may mean that your app does not free enough memory when under memory pressure, which can lead to it being closed by the OS.
You can use viewDidUnload to manage Memory, if your App deserves it.
But don't do a
myInstanceVariable = nil;
You loose your reference to the memory location, where the values of your variable live.
= nil doesn't free the memory. Still, your object has to be deallocated. And therefore the retainCount and retain/release are used.
If you nil your object in viewDidUnload you cannot release in dealloc! Attention!!!
If you know, what you do, you can release and nil your object in viewDidUnload.
ARC:
Using ARC, you must not release manually, even I think you can't do so. ARC takes care of that.
Simply use #properties as much as you can with with (weak/strong) attribute to let getters and setters do the work for you. You can declare #properties in your .m file either (like in a class extension).
Simple rule: strong for objects you want to own, weak for objects, you don't want to get lost in a retain cycle and hold ownership to. ARC does the rest in almost all situations. Use weak for delegates for example.
nil objects, where you are sure, you won't send them a message to, otherwise do not.

Resources