Hi All
I am trying to dealloc a ViewController in ARC mode.
However, the RefCount is always non-zero.
I have tried to set all object to nil and all subviews to removeFromSuperview + nil;
and timer to invalidate + nil;
still the counter = 2;
Is there a way to trace which pointer is still in retain?
Thanks
If you are using blocks you might also create retain cycle there. E.g. a block is referenced by an object and inside this block you are referencing object or calling instance method for object.
Another option for retain count not dropping to 0 is that you have registered abject as observer for notification.
You might find this answer helpful:
https://stackoverflow.com/a/12286739/2261423
Example of strong reference cycle from apple docs:
self.block = ^{
[self doSomething]; // capturing a strong reference to self
// creates a strong reference cycle
};
#Billy, why are you doing this? You may not worry about deallocation when using ARC. Controller will be deallocated automatically, when there will be no references to the controller. Yes, views do not refer to controller, they are referred by it! So removing that views will not affect retain count of the controller. If you really want to remove View Controller from memory, remove it from parent view controller and set all links to nill.
Related
In my app I'm polling a web service for status updates, using a completionHandler block and making changes to the current view based on returned results when the callback executes.
- (void) tickTimer
{
[MyWebService myWebMethod:param1 completionHandler:^(NSString *result) {
// does view still exist?
[self myUpdateMethod];
// does property still exist?
self.theResult = result;
// does child view still exist?
_txtUpdate.text = result;
}];
}
But in the interim, it's possible that the view may have been unloaded as the user navigates elsewhere.
So a couple of questions:
What happens to a view when a new one is loaded and it gets pushed to the background? I imagine it gets garbage collected at some point, but how do I tell if it's still safe to access by any of the references above, and what would happen if it's not?
If the view does still exist, how do I tell if it is also still the foreground view?
So, blocks create strong references to all objects pointers that are referred to in their closure. Due to this, your block is going to force [self] to stay in memory until the block is destroyed. If this isn't the behavior you want you should create a weak pointer to self and refer to it inside of the block:
__weak typeof(self) weakSelf = self;
So a couple of questions:
What happens to a view when a new one is loaded and it gets pushed to
the background? I imagine it gets garbage collected at some point, but
how do I tell if it's still safe to access by any of the references
above, and what would happen if it's not?
If your view stays in the view hierarchy, it will stay in memory. Once there are no more references to the view it will be dealloced.
If you use a weak pointer like outlined above, then [weakSelf] will be nil if the view has been dealloced
If the view does still exist, how do I tell if it is also still the
foreground view?
I'm not sure what you mean by foreground view, but if you want to see if it's still in the view hierarchy then you can check the property -(UIView *)superview. If superview is nil, then it's not on the screen
If you use ARC right, it will not let you use deallocated viewcontroller.
You can use viewDidAppear and viewDidDisappear methods to know visible yours viewcontroller or not.
I pass a completion block to my method, this completion block will be called in the background when a network request is finished. Unfortunately, if the calling object is deallocated in the meantime, the app crashes:
ViewController (which may be deallocated because it's popped from the navigation stack) code:
__unsafe_unretained ViewController *weakSelf = self;
[[URLRequester instance] sendUrl:url successBlock:^(id JSON) {
[weakSelf webserviceCallReturned:JSON];
}];
URLRequester-Code (made simpler, of course):
- (void)sendUrl:(NSString *)urlAfterHost successBlock:(void (^)(id))successBlock {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);
successBlock(nil);
return;
});
}
If, in this 2 seconds, the ViewController gets popped from the navigation stack, the app crashes. What am I missing?
When you use __unsafe_unretained, then the reference remains around even after the object is deallocated. So if the view controller gets popped, then weakSelf is now pointing to a deallocated object.
If you change it to __weak instead, then when the view controller gets deallocated, it will set weakSelf to nil, and you'll be fine. You don't even need to do a check for if weakSelf is set to anything, because calling a method on nil has no effect.
It seems quite a few people think that 'self' inside a block must always be a weak (or unretained) copy. That's usually not the case**.
In this situation the misunderstanding is causing the crash, by leaving a zombie around. The right thing to do is to refer directly to self in the block (not unsafe_unretained, not weak), just like you'd like normal code to look. The effect will be that the block will retain the instance pointed to by 'self' -- the view controller in this case -- and it won't be destroyed until the block is destroyed (presumably by the url requester).
Will it be damaging to the view controller to process the result of the web request even though it has been popped? Almost certainly not, but if you think it will be, check for that condition in the block.
if (![self.navigationController.viewControllers containsObject:self])
// I must have been popped, ignore the web request result
// Re the discussion in comments, I think a good coder should have misgivings about
// this condition. If you think you need it, ask yourself "why did I design
// my object so that it does something wrong based on whether some other object
// (a navigation vc in this case) contains it?"
// In that sense, the deliberate use of weakSelf is even worse, IMO, because
// it lets the coder ignore _and_obscure_ an important question.
else {
// do whatever i do when the web request completes
}
**The need for weak or unretained pointers in blocks stems from the fact that blocks will retain the objects they refer to. If one of those objects directly or indirectly retains the block, then you get a cycle (A retains B which retains A) and a leak. This can happen with any object to which the block refers, not just 'self'.
But in your case (as in many) the view controller referred to by self does not retain the block.
The good practice in using block (especially delayed ones) is to make a local copy of the block in the calling method. In your case it should be done in -(void)sendUrl:successBlock:
successBlockCopy = [successBlock copy];
and then call
successBlockCopy(nil);
It should retain your viewController for a while until completion.
Also it is better to use __weak instead __unsafe_unretained to avoid problems with suddenly released objects.
I've become a little paranoid with blocks and the possibility of creating a retain cycle. I'm using a block based version of the UIAlertView class which allows you to use blocks instead of delegate methods. I use a lot of these Alertviews, so I'm often calling into instance methods that do a lot of heavy lifting.
Would the assignments I make in the method someInstanceMethod cause a retain cycle?
(I am using ARC for memory management.)
__weak id weakSelf = self;
[doWorkAndThen:^{
[weakSelf someInstanceMethod];
}];
-(void) someInstanceMethod{
//will either of the assignments below cause a retain cycle?
self.iVar = #"data";
[self setIvar:#"data";
}
No. -someInstanceMethod is not a block. The fact that you're calling it from one is irrelevant. Only references inside the block itself can cause retains, and since your only reference inside your block is a __weak variable you're fine.
Incidentally, if you really want to ease your mind, you should modify your block-based UIAlertView class to throw away all the blocks when the view is dismissed. This way even if you do create a retain cycle, it will be broken automatically as soon as the alert view goes away.
I've found on Apple documentation pages an example in which they deallocate the memory as follows:
- (void)viewDidUnload
{
self.mapAnnotations = nil;
[super viewDidUnload];
// Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
// For example: self.myOutlet = nil;
}
- (void)dealloc
{
[mapAnnotations release];
[super dealloc];
}
I was wondering why
they first set mapAnnotation to nil in viewDidUnload and then they release in dealloc method and
why they refer using to mapAnnotation with and without self.
This is the downloadable example by the way: MapCallouts Example
The question you should be asking is: when is viewDidUnload called?
In short it's called in low memory situations where you don't want to deallocate the whole controller, i.e., when you don't want dealloc to be called.
You can think of viewDidUnload as the opposite of viewDidLoad of loadView whereas dealloc is the opposite of init. (In practice it can get a bit more complicated than that of course.)
viewDidUnload can be thought of as the opposite of viewDidLoad. It is called in cases where the view is unloaded due to memory warnings, but the view controller is not actually deallocated.
It allows you to release memory that is only relevant when the view is loaded, and therefore allows you to free up memory in these low memory conditions.
As for the difference between the two releases, one using self and one not:
In the viewDidUnload method, the variable is being set to nil via it's accessor methods. The variable has been declared as a property, likely with the retain attribute. When assigning nil via a property, it's functionally the same as the following code:
- (void)setMyObject:(MyObject *)object
{
[myObject release];
myObject = [object retain];
}
So if you're passing nil to that method, you'll be releasing the old instance and assigning nil to the variable. Assigning nil after releasing is considered good practise to prevent any possibility of trying to dereference a dangling pointer.
In the dealloc method, the variable is accessed directly, as opposed to via an accessor method or property, and is released. There is not really any need to assign nil at this point because the owning object will be deallocated and will not be accessing any of its pointers, making the possibility of dereferencing a dangling pointer very low.
(1) This is done so you can let go of references so the system can deallocate resources in low memory situations. It is the reciprocal of viewDidLoad:
(2) If you refer to self using dot notation, you will maybe create an object that is the implementation detail of the property. Using the name ensures that you release the object itself, and not the object returned by the property. In the first example, in the case of an outlet, assigning nil with the dot notation (self.) will decrement the retain count of the retained property.
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?