When a request is sent from a viewcontroller having a completion handler in place, but before the request can be received by the device, we go back to the previous view. What will happen to that completion handler block?
When are objects deallocated from memory?
Is it when the related object is moved away from the screen? Wrong!
Or is it When the related object is removed from memory? Right!
All objects are removed from memory when 'there is nothing left to strongly point to them'. This could happen when the view goes offscreen. See AutomaticReferenceCounting from docs.
Closures or completionHandlers, point to the instance. Hence they will hold the instance in memory. The instance itself will also point to the completionHandler. So both would be waiting for the other to be removed from memory. As if two people each say I would like you exit the house first. Ending result is that that none of them leave.
You need to avoid that from happening. As Sh_Khan said you do that with [weak self]. For more on why we do that. See Reference to property in closure requires explicit 'self.' to make capture semantics explicit
This case is the reason why we need to put [Weak self] in completion , to avoid strong references to that vc and so to be deallocated , if you skipped it , then what you did in completion will run but you'll not see anything as the vc's view is hidden after the pop while this instance is still in memory and will cause memory drains by time
Related
I have a view contoller (for log in) that defines deinit method to remove observers. When user taps on Register button, the controller calls performSegueWithIdentifier to load registration view controller. I defined deinit method to remove all observers. However, that method is not being called. I read it somewhere that it is because the viewcontroller is not being destroyed and the pointer to it is hold somewhere. Could anyone explain the reason behind this?
Thanks
EDIT : Although I agree that the link provided in the comment section holds the same question, there is no answer or clear answer to that. The highest upvoted reply suggested to define something inside deinit instead of leaving it blank. That does not answer the explination I'm looking for. For that reason, I'm keeping this question until somebody can point out my understanding is incorrect.And also, I do think that Matt explains it succinctly and clearly.
The usual reason for failure to trigger deinit when expected is that you have a retain cycle that prevents your view controller from going out of existence.
(Sometimes the reason is that your expectation that the view controller would be destroyed under the circumstances is incorrect. But assuming it is correct, a retain cycle is the reason.)
You mentioned removing all observers. What kind of observers? If we're talking about NSNotification, that is often how you get a retain cycle. The notification center retains the observer until you unregister it. You thus cannot get deinit until after the observer has been removed. Therefore, you cannot remove the observer in deinit.
I am developing a game for iOS using Swift. The game is played in rounds, and when a round is over, it is no longer needed. So unlike conventional applications where a storyboard scene may stick around when someone navigates away from it, I want the scene for the round to be destroyed when the game navigates to the scene following the round.
I am pretty confident I have figured out a way to do this because I’m instantiating certain objects in the scene and I have verified that the deinit method for these objects gets call when the round ends and navigation to the next scene occurs. The problem is that for one of the objects, the deinit method is not being called, and I have not been able to figure out why. The storyboard scene should be the only thing referencing the object, and searching through to source code has not revealed any other usages. It seems like something beside the storyboard scene somehow has a reference to the object and keeps it alive when the scene goes away.
I have verified the object is being created by setting a breakpoint in its init method, and that breakpoint gets hit when starting the round.
I’ve tried using the Allocations instrument to track what might be referencing the object. However when I run the game and finish the round. I can find no evidence that the Allocation instrument ever saw the object at all. I suspect the instrument may not track objects that are created by a storyboard scene, especially since I can not find traces of the other storyboard objects that do get deleted with the storyboard.
What can be done to determine why this object seems to survive past the lifetime of the storyboard scene that should own it?
I think found out why the object was not being destroyed. In it's initializer it was applying a closure to something else. The closure contained a reference to a member of the object, and so I think the closure was capturing self strongly even though self was never explicitly needed for things to compile successfully. This might be a bug with the Swift compiler; it could have emitted a warning or error about this; it would have been easier the find and/or prevent the problem. Anyway I changed the closure so that it captures the object's member as unowned and now the object appears to get destroyed.
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.
SOLVED BELOW
I'm reading this article from raywenderlich blog:
http://www.raywenderlich.com/23037/how-to-use-instruments-in-xcode
to learn about instruments and figure out if I´m doing something wrong in some old projects.
I've seen that in one particular point of my code, when I'm showing a modal view that eventually is closed, the memory allocated remains there. As you can see in the following image.
The execution have 4 marks generated.
Between the 2n and the 3t mark, the view is showed, as you can see, new memory is allocated.
But between the 3t and the 4th, I've called dismissViewController, and the view no longer remains. But the memory remains allocated.
All the properties, are created as strong (probably no the best approach):
And I´ve an NSTimer, that is initialized in viewDidLoad method, and set to nil at viewWillDisappear:
[self.secondTimer invalidate];
self.secondTimer = nil;
So, do you have any idea about what's happening? From what I know, even the properties are declared as strong, when the UIViewController is released, all of them are going to be released to.
EDIT
Thanks to all, with the information I provided, wasn't enough.
As you can see, QRViewController inherits from BaseViewController.
This controller had a delegate defined as strong storage, terrible.
So that's all.
In the view controller hierarchy, self.view holds ALL his subviews with strong, so everything under self.view (Probably all your IBOutlet properties) can switch to weak. That probably won't solve the problem though.
What might help you is the fact that any block you have holds every single object used in that block as a strong, to make sure the block can run it's code at the time being. If nothing holds that block (like a animationWithDuration:) than no worries. But if you have any block that an object is holding (Like and object's "completion-block" or any other creative use of blocks), everything within that block will be strong, and there's a chance you create a retain cycle that way. For example: the presenting view controller is calling the presented view controller with a completion block, and in that block you use self. Now presented VC is holding a block to perform on dismiss, and the block holds the presenting VC. When dismissed you will end up with a VC that holds a block that holds a VC that holds the presented VC....
A simple solution would be to give the block a weak version of self and only when the block executes, make it strong for the time of running the block (To avoid dealloc while running the block):
__weak myViewController *weakself = self;
[self.someObject setBlockHandler:^(BOOL foo){
myViewController *strongself = weakself;
if (strongself) {
// Do whatever...
}
}];
It's difficult to pinpoint precisely the problem, but usually when things like this happen to me, it winds up being one (or a few) "root" culprits -- you find that one, clear it up, and then lots of others clear up too. So one strategy you can try is to sift through the Instruments data looking for any sort of "hierarchy" (think about how your app is structured and how the objects relate to each other) and look for objects closer to the base, then cross-reference against your code to see if they might have a retain cycle or some other such issue.
One immediate change I would make would be to change your IBOutlet declarations from strong to weak. For the most part, IBOutlet properties should be weak, for objects that are within a hierarchy. So if say you've got some UILabel within your xib's main view, that label should be weakly-retained so as to avoid a retain cycle. But if say that UILabel is standing alone as a root item within the xib, then it would need a strong reference. I'm going to guess most (if not all) of your IBOutlets there are within a hierarchy, so make them weak and try again. It may not solve all the leaks, but see if it makes any difference.
This is called Abandoned Memory, check this link.
TIP:
If you are navigating between view controllers, and you perform the navigation inside a closure, you should use a weak or unowned version of self, example:
//Swift 2.1
//Performing naivgation on the main thread for responsiveness:
dispatch_async(dispatch_get_main_queue(), {[weak self] () -> Void in
if let weakSelf = self{
weakSelf.performSegueWithIdentifier("myOtherView", sender: weakSelf)
}
})
Also, when dismissing the view controller is the same:
dispatch_async(dispatch_get_main_queue(), {[weak self] () -> Void in
if let weakSelf = self{
weakSelf.dismissViewControllerAnimated(true, completion: nil)
}
})
The posted link above shows a practical example on how to catch abanodend memory using Xcode Instruments.
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.