I'm pushing and popping ViewControllers in UINavigationController.
I'm tracking the memory consumption of my app.
While pushing the new viewController the memory consumption is increasing gradually, but when I'm popping the same ViewController using [self.navigationController popViewControllerAnimated:NO]; the memory consumption does not decrease but the constant.
That particular viewController can be pushed and popped by user many times which can lead the high memory consumption of app in RAM.
What should I do to optimise my memory consumption?
When you dismiss a view controller (or pop it), it will be deallocated if you didn't make any strong pointers to it (that controller is retained by the navigation controller, or the presenting view controller, so you usually don't need to have a pointer to it when you create it and push or present it).
It will be be released if there are no other strong pointers to it
Try to avoid using strong properties for IBOutlets.
Consider reviewing whether you are referencing self in a block. If you do, you risk holding onto the UIViewController reference after you have popped it.
For a more in-depth review of why, check out this answer:
How do I avoid capturing self in blocks when implementing an API?
If your app design allows the user to push and pop the same view controller over and over again, you may want to look at reusing the same view controller and just updating its contents each time it's pushed.
Instead of creating and destroying it over and over, create one, set up its contents and push, when it's popped, keep it around ready to be shown again. Next time it needs to be shown, update its contents and then push it again.
I would like to say, that my last few days were spent on searching the web for my app memory problem. I was switching between 2 UIViewControllers. One of them had a scroll view which kept all subviews on it. It turned out that that UIVC loads a new scroll view without releasing the previous one. It took me several hours to realize it.
What I did was:
Looking for any kind of deadlocks inside the app, then searching for every variable that had a strong atributte and other desperate measures. But what really worked was:
#IBAction func backBB(sender: UIBarButtonItem) {
collectionView.removeFromSuperview()
self.frontView.removeFromSuperview()
eventsPhotos.removeAll(keepCapacity: false)
symbolContainerView.removeFromSuperview()
self.myScrollView.removeFromSuperview()
dismissViewControllerAnimated(true, completion: {})
}
I manually removed some views and contents. I've done it in "Back" button but you can do this in other methods like viewWillDisappear(animated: Bool).
Once I made this, my allocation chart in the developer instruments showed the memory allocation going up and down... And it was solved...
I think you get an error, when you try to pop the view controller because the navigation controller does not have a valid reference to the view controller, as it was released after you pushed it.
Nil the popover on dismiss.
[menuPopup_ dismissPopoverAnimated:YES];
menuPopup_ = nil;
Make sure your viewcontroller (A) has no reference of any other viewcontroller (B) or any object it has. Incase it has then make sure that VC-B is not referencing back VC-A. If it has some reference back to VC-A then make it a weak property. Otherwise strong retain cycle will keep the VC in memory even if its popped.
Another thing is to check wether theres any closure in your VC, check its body if it is referencing any property or method with self then make a Capture list to avoid retain cycle as "Closures are Reference Types"
Check if theres any NSNotification observer you are not releasing
Make a print call in deinit method to check wether its deallocated.
For further understanding on memory management:
https://medium.com/mackmobile/avoiding-retain-cycles-in-swift-7b08d50fe3ef
https://www.youtube.com/watch?v=GIy-qnGLpHU
You should use an unwind segue instead of popping.
Related
I have two view and second view is pushed from first view. For testing, I go to second view from first view and then I go back to first view. After that, I send nsnotification and in my second view, it receive my notification.
1) May I know why I receive notification in second view after it is pop up? For IBOutlets, I declare weak property also.
2) If it is still in memory, for other data like nsdictionary, nsstring, shall I use strong or weak property? Will those also in memory?
3) If I don't want my second view in memory totally, how shall I do?
If your second view controller is not released when you "go back" to the first view, then either
You are not really "going back" - you are accidentally creating a new first view controller and pushing it, which is unlikely; or:
You have a retain cycle in your second view controller.
I'm betting that you do have a retain cycle. You should try to track this down. You mention notifications: it is very easy to create an accidental extra retain when setting up a view controller as a notification observer, so that's probably the cause.
In particular, see my book's discussion of this topic:
If you called addObserverForName:object:​queue:usingBlock:, you will leak (under ARC) unless take elaborate precautions (such as doing the weak-strong dance in the block, to avoid a strongly retained self).
I'm using the following code to push a new view controller into the stack, the ViewController is initalised with an nib and only contains a webview, the class is completely empty.
var profileVC = GenericWebViewController()
profileVC.initWithURL(url)
navigationController.pushViewController(profileVC, animated:true)
When the view is pushed, memory usage goes up, however, when the back button is pressed, the memory level does not go down.
What should I do to get profileVC garbage collected?
Nothing ;)
Automatic reference counting automatically handles this for you
http://clang.llvm.org/docs/AutomaticReferenceCounting.html
Edit: The memory may not be going down for other reasons. Check you don't have any strong reference cycles. To be 100% sure your GenericWebViewController is being cleared from memory, override its dealloc method and make sure it's being called.
Also check out the leaks instrument
my project use ARC, and when i have to show a view with navigation controller i do this:
ShareViewController_iPhone *share = [[ShareViewController_iPhone alloc] initWithNibName:#"ShareViewController_iPhone" bundle:nil];
[self.navigationController pushViewController:share animated:YES];
and i can see in xcode that the memory is increased of a certain value, then when i dismiss the view i do this:
[self.navigationController popViewControllerAnimated:YES];
but when the view is closed, the memory doesn't decrease, how i can do it?
When you pop the viewcontroller, it is marked for deallocation (i.e it's reference count becomes 0 unless referenced by another object). but not deallocated immediately. Deallocation as far I have seen is mostly random!
The view controller here must be retained for a while by the navigation controller itself to create the animation. The deallocation does happen immeadiately after the reference count drops to zero. Try to pop the view controller without animation (e.g. animated:NO) and see for yourself if the deallocation happens right away.
ARC will clean the memory on its own cycle (it will collect all the objects whose reference count set to "0" means who are ready for deallocation), we can't control this. It is managed by the operating system.
So when you are popping a view controller, it doesn't deallocate it from the memory immediately. It can take time depending on the other memory usage/memory availability.
One more case, you need to check if on every push the memory allocation is increased by a certain value, then there will be definitely something wrong in your code.
When you pop a ViewController and it don't release, it means have a reference cycle in your controller. You must check delegate if have, block, weak, strong reference.
Your delegate must be weak reference.
If use block. The self must be weak reference: __weak id weakSekf = self;
Hope these information will help you. :)
I have three view controllers pushed in navigationcontroller on the third one i used the statement
[self.navigationController popToViewController:(Main_View*) mainViewObj animated:YES];
It takes me directly to my specified controller say first. I have done some coding to remove objects from an NSMutableDictionary in viewWillDisappear method in all view controllers, i tried debugging using break points but it never comes to viewWillDisappear, it takes me directly to Main_View. Should i be worried about removing objects from dictionary or releasing it?
The viewWillDisappear: method is not called for the other view controllers because they have already disappeared when you were pushing view controllers on top of them. So basically, viewWillDisappear: was already called for them at an earlier point. It wouldn't make sense to call it again, because they weren't visible in the first place.
You can try to keep weak references to your NSMutableDictanories in your AppDelegate, then after poptoviewcontroller in mainViewController get them, and see, if they are nil, or not, and if not, you can remove objects from Dictionaries in your main view with that references.
I have a stack of UIViewControllers currently, each is a modal ViewController presented over the previous one. My problem is that I do not need a stack of UIViewControllers, I only need the last one. So when a new UIViewControllersis presented, its parent should be purged, deleted completely from memory. My app will never need those viewcontrollers again.
I have read this: http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ViewLoadingandUnloading/ViewLoadingandUnloading.html
But this pattern only cares with memory freeing if the app gets a memory warning. And doesn't purge viewcontrollers, only their content. I would like to do it in a more manual manner... Is this possible, or it is not a common practice in iOS, and I should rely only on memory warning messages.
The easiest way is to avoid creating the stack in the first place. Instead of presenting new modal controllers over existing ones, have your root controller dismiss the existing one first and present each new one.
I might have found a nice solution. One should use a UINavigationController, and manage its viewContollers property (which is the stack of UIViewControllers) manually. After (or before) the new UIViewController loaded, you can delete the old UIViewController from the stack, and thus purge it from the memory.
This post helped:
How can I pop a view from a UINavigationController and replace it with another in one operation?
This way you can make a program flow, where only when UIViewController is in the memory at a time, and you replace them when you need a new UIViewController.