didReceiveMemoryWarning using a Tab Bar - ios

I have a UITabBar holding 3 UIViewControllers.
One being a map, the other - a table, and the third - my own custom controller.
I noticed lately that my app has some issues when it comes to operating on low memory, and it's due to me not being clear about what to do when didReceiveMemoryWarning occurs.
Usually, and when with sufficient memory, all functions great. I would alloc all my controllers on the applicationDidFinishLaunchingWithOptions and all controllers work great.
But what happens when the memory is a bit low is that (for some reason) only my table is misbehaving.
First, I can see that the app received a memory warning. and then only my table gets a viewDidUnload.
At first I wasn't sure why my table became *completely empty*, but then I realized that my delegation methods stopped working rendering my reloadData essentially pointless.
So, I can see now that the didReceiveMemoryWarning is cascading through all my viewControllers. But viewDidUnload is called only on my table.
I want to know what is going on?
How do I recover from a viewDidUnload? If my view is nil, who is responsible for bringing it back?
Why is it that only my table is getting the viewDidUnload? I was reading this link and that one, and some of apples documentation but found no explanation to why my view doesn't recover.
What should I be doing in this case?

Related

Keep memory clean and kill ViewControllers

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.

iOS Show UIViewController as Popup from Anywhere

Basic question:
Is there a reliably way to trigger showing modal UIViewControllers at any point in the app's lifetime (including from different threads)?
My current approach is to call presentViewController on the showing ViewController (found through window.rootViewController + hierarchy traversing but that's unimportant). This generally works, but is sometimes ignored due to things like a navigation action/animation taking place.
E.g. a background thread signals for a popup to be shown, and presentViewController is called on a ViewController in the process of being dismissed.
I've tried a few work arounds such as repeating the signal if the ViewController isn't shown (which led to some instances of it being show multiple times), but it's ended up being a game of whackamole.
An ideal solution would also allow navigation to take place underneath the popup, but the primary issue right now is just reliability.
edit
To be clear, I’m a seasoned developer. The threading is being handled properly, the instance and type management is working. My problem is trying to manage all
the corner cases, not the basics of how to do it.
If you need mechanism for thread safe showing different VCs in multithread environment, you can make some object which is responsible for presenting/dismissing controllers. And make some queue on presenting/dismissing. So when your signal occurs, your operation on presenting will be next after dismissing current VC in queue

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.

How to isolate the cause of strange iOS app crash

There's a crash in the app that I'm working on that I'm having a difficult time tracking down the cause of. Here is the only set of events which causes the crash:
The app opens with the main view controller (VCmain) being presented. A button is triggered which opens a modal view controller (VCmodalA) via a segue. VCmodalA simply displays some information gathered about a core data object. VCmodalA is then dismissed by a button press. At some future point another view controller is presented modally (VCmodalB), which has some fields for the creation of a new core data object. After the object is created, if VCmodalB is dismissed, the app crashes with the following error:
*** -[UILabel _supportsContentDimensionVariables]: message sent to deallocated instance 0x818e200
If VCmodalA is not displayed prior to VCmodalB, or a new core data object is not created, or VCmodalB is not dismissed, then no crash occurs. I am at a loss what '_supportsContentDimensionVariables' means or who is sending it, although it appears to be sent to a label which was on VCmodalB (therefore the crash must be caused because a call is being made to a label that was deallocated when the view controller was dismissed).
I've spent hours poking around in Instruments looking at the Zombie left behind and trying to isolate the offending code by commenting it out, but I have been totally unsuccessful. At this point any hints would be welcome!!
Thanks so much!
There are two top causes of these kinds of errors: failure to use ARC, and direct access of ivars (particularly if you're not using ARC). Fixing those two issues is the best way to avoid these kinds of crashes.
As for how to debug it, first, you want to audit your accesses to UILabel objects. If you have any ivars that point to UILabel they should be strong or weak, never assign.
You should make sure that view controllers don't run code when they're not onscreen. This crash makes me think that this is a likely problem. For example, do not setup timers in viewDidLoad or initWithFrame:. Set them up in viewDidAppear: and tear them down in viewWillDisappear:. Similarly for KVO or delegation. View controllers manage views; if they're doing something when their view is not onscreen, your design is incorrect.

How to use didReceiveMemoryWarning in UINavigationController stack

My app has several UIViewControllers in a stack (under a UINavigationController). In certain cases I get memory warnings when I'm in the inner UIViewControllers (mostly happens if the device is overloaded with other applications running in the background). When these memory warnings occur the application keeps on running (most of the times) and then when it gets back to the rootViewController it reloads it but doesn't fill in the view objects (mostly UIButton images).
I wish to implement didReceiveMemoryWarning and applicationDidReceiveMemoryWarning, but I'm not clear regarding to how to do that.
The end result I'm looking for is that when the application does this "reload" after memory warning it will "reload" rootViewController in the same status it was before the user started "diving" into the inner UIViewControllers.
How should I do the implementation?
Should I implement the default didReceiveMemoryWarning in each UIViewController?
Since the memory warning always occur in the inner UIViewControllers, how should I let the rootViewController know that it should run didReceiveMemoryWarning?
How do I tell the rootViewController which settings it should do? In other words - can I keep the settings someplace where didReceiveMemoryWarning doesn't delete them and upon activating didReceiveMemoryWarning "recall" them so the user will see the same screen they started the "levels navigation" from?
Any non-visible view controller on the UINavigationController stack will dump its view. It will also send you the warning. If you have large data structures being held by the view controllers you should dump those if possible.
The root (an other) controllers should appropriately handle viewDidUnload and be able to cycle through another loadView/viewDidLoad phase.
If you want the state to be the same, you need to be persisting all that information. NSUserDefaults is a standard location to do that.

Resources