Keep memory clean and kill ViewControllers - ios

I've noticed that as I navigate across my iOS app memory stack grows with each opened viewController.
My navigation is based on segues and I do self.dismiss() before performing each segue. Still, it looks like viewControllers stack in memory.
How can I avoid this?
In Android finish() kills the Activity (in most cases), so I need an alternative in the iOs.

The memory issue can have several causes and not necessarily a UIViewController. In order to find and solve the issue you have to reduce the scope of the issue from "app" to a certain screen or even class. Then you can check the code and try to figure out where's the suspicious code.
Solving this issue is not a straight up task and there's no "how to fix memory issue for my app" tutorial, you'll have to check your code and compare with potential causes of memory leaks.
Also you'll have to be careful for false positives of memory leaks. Here are some steps I follow when I suspect a memory issue.
Navigate trough the app "till the end", then go back to "home screen", if the memory drops, all good.
If the memory doesn't drop, I do the same navigation several times, if the memory increases with the same step (more or less but close) then there's an issue. If the memory doesn't increase (maybe just a bit, several kb) then it's ok, it means there are some assets cached in memory (images, files, etc). But you will need a way to test this scenario too.
Now we are back to the "memory increased again almost the same as first time", now I do a clean run, and take each screen at a time, I just open the screen go back (dismiss/pop the controller) and observe, if the memory drops then that's not the screen that leaks. When I find the screen that increases the memory and never goes back:
check if the controller is passed as a reference to other objects that won't be deallocated (singleton classes or other, depends on the app).
check if the controller is sent as "delegate" to any other classes and if those delegates are correctly defined (strong delegates are a biiiiig issue).
if all of the above are ok, then I'll simply comment all the code that performs any work and try again. If commenting the code doesn't drop the memory(this happens rarely) then the screen is not the right one.
if the memory drops after commenting the code, I start de-commenting bits of the code and run again, until you'll find the piece of code that creates you issues.
One thing to keep in mind, you have to be patient while investigating memory issues, and to be honest, sometimes you have to be lucky too.

Per documentation on UIViewController.dismiss:
Dismisses the view controller that was presented modally by the view controller.
So calling this would dismiss any view controller shown modally. Based on your question, I have to assume that your segues are push segues and not modal, otherwise you'd be seeing your view controllers disappear.
Your 'view controller stack' might be with regards to the navigation stack on a UINavigationController. If so, then those view controllers remain in memory, as when a view controller is popped off the stack (ie: user swipes from left edge of screen, or hits "Back" in the nav bar), the previous view controller appears.
Without more information on how you're presenting your view controllers or building your navigation, I don't think you'll be able to get more answers.

Related

iOS Swift 4: ist it more efficient to dismiss or present a ViewController

In an iOS app, to move between screens, I can either present a new ViewController ("move forward") or dismiss a current ViewController ("move backward").
In my naive understanding, this is simply a way of moving back and forth in the stack of ViewControllers kept by the app.
I have an intuitive preference for dismissing a ViewController (where possible) rather than presenting a new ViewController. It gives me the feeling of operating within a finite set of ViewControllers which in turn makes me feel the app is memory efficient.
Say I am on View A and want to show View B, then presenting A would result in a stack A-B-A whereas dismissing B would keep the stack at A.
My question is this: is that justified? is there any (programmatic) downside to perpetually working by presenting new ViewController? Is it memory inefficient?
I wonder how many previous Views are being saved by the app and how long the stack could get, and if that is a reason to dismiss whenever possible.
I am not 100% sure if i understood you correctly, but it seems in-efficient memory wise and not sure how possible it is. You want to keep going backward basically, just dismissing views, right?
Meaning when you load B, then A, then you want to dimiss A to go to B again? Am i understanding this ok?
In that case views would have to be in a constant "stack" and i am sure it will make things go slower, but more imporatantly, from user perspective and user experience, something that they are not used to at all :/
My understanding would be: If you want to show A again, then you should be going back to the instance of screen A that you already have. There might be special cases where it makes sense to have multiple instances of A (like detail screens for different objects), but even then: What you definitely should not do is build a never-ending stack of view controllers because as you already assumed: This will consume lots of unnecessary memory.
Maybe you should take a look at existing apps or the Apple Human Interface Guidelines and try to understand better how their view hierarchy works.

Is there a way to check if too many VC are stacked

I wonder if there is a way to check if I have too many views stacked in my app.
My app design is:
Navigation controller -> table view (table view works like root VC)
From the table view I can open the menu VC as a modal segue, and form there I can open the login VC as a modal segue, if I log in I end up on the account page like:
Navigation controller -> table view -> menu -> login -> account page
From the account page I can go deeper:
Navigation controller -> table view -> menu -> login -> account page -> list settings page -> edit settings page
Now I have 7 VC's stacked if I count the navigation controller, even though two of them are displayed as modal VC's.
My app does not crash but is this a good way to do it? If I understand it right apps now have to share CPU when running split screen on ipad, so I am not sure if this way is eating too much memory.
Or should I simply make the account VC become the new root VC and reset the stack? And when going back to the table view make that one the new root VC again.
View Controllers are light weight objects. If your concern is memory you should react to memory warnings. E.g. in the viewcontrollers' didReceiveMemoryWarning method. You could release any cached images, remove views if the VC is not visible. Releasing cached objects and images is not always desirable in viewDidDisappear as the user might come back to the screen and you want to avoid reloading everything if there are no memory issues. didReceiveMemoryWarning is the correct place to help the system in freeing memory without sacrificing user experience.
You can of course check how many view controllers are on a navigation controllers stack by checking the UINavigationController's property viewControllers.count.
Resetting the stack to always only have one root viewcontroller is nice but usually quite complex. It is also not really possible inside a navigation controller.
You should use Instruments to check your memory consumption and to verify that you correctly react to memory warnings. Memory warnings can be manually triggered in the simulator.
So when views are stacked on a navigation controller, they are not fully de-allocated from memory, even when they are offscreen. There isn't a strict maximum number of stacked views but you should be smart about it.
If you are concerned about RAM usage then be sure to utilise viewWillAppear and viewWillDisappear to their full potential, performing cleanup operations such as nulling off gesture recognisers and observers, and stopping any listeners that may be running on a background thread. This will reduce the amount of memory your views are using when offscreen; and is good coding practice anyway.
Hope this helps somewhat.
EDIT: Felix also raises a good point about memory warnings, if iOS is concerned about the memory usage of your app it will issue a memory warning which you can react to in the way felix explains.

IOS Push many view controller without pop

I wanted to know what will happen if we keep pushing same view controllers again n again. I have 4 buttons, each of which triggers a View. All the 4 buttons are there in all the 4 views.So each time I click a button a view is loaded. So I am pushing a view controller. will this lead some kind of memory management issue or nay other issue? Any other way to handle this? I cant use Tab bar cause of design issues.
I WANT TO BASICALLY IMPLEMENT A TAB BAR WITH 4 BUTTONS. I CANT USE A TAB BAR DUE TO DESIGN ISSUES
This won't lead to any specific memory management issue. Every time you allocate an object it takes up some memory. When you push the view controller you are just allocating a new copy of that object. Whether it's a view controller or a data model or a string, each object takes up some memory. You can easily profile how much additional memory gets used each time you push the view controller, but most likely it is negligible (probably much less than 1kb depending on how much you have in there). I just profiled one of my view controllers and it used 320 bytes. So for simple math, let's say each push takes up 1kb of memory. And an iPhone 5 has 1Gb of RAM. That's enough to hold about 1 million view controllers. So I wouldn't worry about it.
However, if you want to worry about it then you should implement didReceiveMemoryWarning in your view controller and release any unneeded objects.
You can add back btn from your 4 other controllers. On back its removed from navigation stack memory is reclaimed .
If you still have to manual way of managing it, you can use [self.navigationController viewControllers] to check if viewController is there or not and take appropriate decision.

Memory problems when pushing many view controllers on UINavigationController

I'm making an interactive book for the iPad and am using UINavigationController to implement the navigation between a page and the next. When a user turns the page, the next page is pushed on top of the navigation stack.
I'm now 15 pages into the app, and the app crashes when I try to go from page 14 to page 15. No error message in the console, nothing in the device's crash logs neither.
Each view controller's scene in the storyboard has UIImageViews displaying images that are between 5MB and 20MB. The view controllers' viewDidLoad method is called just once. The total size of all the app's assets is below 200B. I'm using ARC.
I've ran the app using Instruments' Memory Monitor. The app's Real Memory consumption increases by about 80MB every time a new page is turned, and crashes when it reaches 800MB (the device is an iPad 3).
Why such an enormous memory consumption? Is this because the UIImageView in the Storyboard's scenes cache the images?
What would be the best way to free up memory when you use a
UINavigationController and ARC?
I tried adding setting all the view controller's subviews to nil in the view controllers' viewDidDisappear: method, but memory consumption stayed the same.
When you use a UINavigationController, each ViewController you push stays in memory forever (well, until your app exits) unless your user presses the back button on that particular ViewController. It keeps a stack of ViewControllers - with the visible one at the top.
So the simple answer is don't use a UINavigationController for this.
I'd suggest building your own ViewController that 'knows' which is the next and previous pages and manually loads and removes them as and when required. This way you can ensure that you only have one page in memory at once (except for during the transitions - maybe you could use this animation for the transitions http://cocoacontrols.com/platforms/ios/controls/xbpagecurl).
You probably don't want to use a UINavigationController for this purpose. You really want one view controller to manage all your pages and render the new page on the same view while removing the old.

Calling the ImagePickerController (SourceTypeCamera) unloads other pages (other tabs) in tabbar controller

I have 2 tabs, one that contains the imagepicker controller, and another that contains a UItableView as a subview to a uiview controller.
I realise that when I call the imagepicker controller (SourceTypeCamera), my page in the other tab is being unloaded ('view didUnload' method) is triggered.
Is this a normal behaviour I should expect? (that other tab's views get unloaded) Or is it due to some memory issue when using the camera which I should take care off?
As you say, viewDidUnload can be triggered at any time, and is normally associated with a low memory warning, a view controller's view otherwise being cached even when not needed for as long as memory will allow. So in a strict sense it's neither expected nor unexpected, as it depends on the sum total state of the rest of the system.
That being said, UIImagePickerController is famously quite memory hungry when capturing an image (see e.g. this image, where each spike is related to an instance of UIImagePickerController "capturing an image" per the related blog), so other views being forced from memory shouldn't be so surprising.
There's no documented mechanism and no reason for a UIImagePicker to force other controllers to dump their views speculatively.

Resources