Get rid of ViewControllers - ios

I've been working on this project for quite some time and I'm almost finished fixing up everything except one thing: memory leaks from viewcontrollers.
Here is my situation : I start in the main root view controller and navigate through VC's(root -> Levels -> Level1) like this
Level1* level1 = [self.storyboard instantiateViewControllerWithIdentifier:#"Level1"];
[self presentViewController:level1 animated:YES completion:nil];
This works fine and all but when I use this way to switch between 2 VC's multiple times, I create the new VC every time and the initial one does not get destroyed.
For example, I'm in level1 VC, decide to go to Upgrades VC, therefore using the methods from above. In my hierarchy of VC's, level1 is still alive when I'm in Upgrades scene. When I go back to the level1 scene, a new one is created with the old one still alive!
Now repeat this process several times and I can find myself with quite a few scenes of level1 VC and Upgrades VC stacked up because the initial VC doesn't get destroyed.
I thought of 2 fixes to this problem: Either I somehow destroy old VC's, which I don't know how, or use a navigation controller, which I don't know how either. I'd really prefer to do it the first way, because with a navigation controller, I fear I have to change quite a lot of stuff on how I navigate through controllers.
What I tried:
[self removeFromParentViewController];
Didn't do anything. I also tried to point the old view to nil( and all properties) once the new view was up, but this did nothing either. My VC's are still alive. If I try to point the view to nil before presenting the new VC, then I get this and a black screen :
Warning: Attempt to present on whose view is not in the window hierarchy!
Edit: I forgot to mention, I'm using ARC so I can't just dealloc stuff and all my views and VC's are done through coding, nothing in storyboard except for 2 things: In the storyboard, I put the VC in landscape mode, what class it inherits from and its storyboard ID

Related

ViewController allocation and deallocation problems

I have this 2 "Loops" in my App between my ViewController.
First Loop is the Game-loop. At first VC my Level is displayed. The second VC is my Game Screen and if the game is finished, my third VC will appear with bonus point, stars, and so on.
The second "Loop" are just three VC with swiping.
Ok, where is now the problem? i have problems with the deallocations. for example, overtime i swipe, my locations are going up in sínstruments, the curve is getting higher and higher...
also my game loop. i can't deallocate the vc before.
i think i didn't understand correctly how [self dismissViewControllerAnimated:NO completion:nil]; works.
is it right, that this method is always sent to the parent vc, and the parent vc deallocate the vc where i execute this method?
is the parent vc my initial vc?
how can i dismiss and deallocate my view controllers correctly in my "loops"?
now, my allocing curve in instruments is getting higher and higher at each level, and about level 18-21 my app is crashing, i think because of too much allocations.
can anyone tell me hoe i can solve my problems?
To begin with your questions:
Is it right that this method is always sent to the parent vc, and the parent vc deallocate the vc where I execute this method?
From the Documentation about dismissViewControllerAnimated:completion:
The presenting view controller is responsible for dismissing the view
controller it presented. If you call this method on the presented view
controller itself, UIKit asks the presenting view controller to handle
the dismissal.
Is the parent vc my initial vc?
Parenting is a bit complex with your structure. Normally, when a VC (let's call it X) presents another VC (this one is Y), X would be the parent of Y. But you must share additional code such as how you're allocating these VC's, how/when you're presenting them, etc. to know which one is the "most" parent.
When you call [self presentViewController:Y animated:** completion:** on X;
X would be the presentingViewController.
Y would be the presentedViewController.
Hence, X is the responsible one for Y. Parent :) So, your question is a bit invalid for your scheme. However, you can easily say that your initial VC would probably be the parent of all VC's that's presented by him. (For example, if you embed navigation controller for VC's, it would be the root VC and it will be responsible for "winding/unwinding", which makes it "parent".)
Normally, view controllers should not go in a loop between themselves. They can be dependable on each other, but it shouldn't create loops.
Solution proposal:
It seems to me that you're duplicating these 3 kind of VC's every time a level begins. That's the first loop's problem. The second loop is a bit complex, I assume you want to save the state of the VC's while swiping to the other VC's.
1. Manipulating current VCs.
These VCs must have an option to clear his state, everything should be reset like it was loaded for the first time. You will only create these VCs once, and reset them if needed.
2. Create a Singleton Class which will hold these VCs.
(Caution: Since this solution involves holding VCs up in a class, they will always hold up memory. They won't be drawn to window, though.)
When you start the app, a singleton will be created, and you will also create the VCs needed at the same time. This class should have methods like:
addViewControllerToStack:
showViewController:animated:completion:
resetViewController:
And of course properties to hold these VCs.
3. Control through Singleton
When the user presses up a button like "go to X level", you must interact with the Singleton class you've created. If you're going to dismiss a VC, the Singleton should do that. Also for presentation.
Summary:
With this method, you cannot create multiple VCs of the same type. You can always use the one which is created by your Singleton class, and you can always reuse them. But be cautious, since you're not deallocating any VC, you may end up with residues (like forgetting to reset/remove timers, to reset score, etc).
I've used it in one of my project, which required a view controller with OpenGL. I couldn't dismiss it since it's always doing calculations. And it could've been created only once. The bad part is, it was in the middle of the VC segues. So, whenever I needed to present that VC, I call my Singleton class to show it for me. And I create it only once.

Storyboard segues and receiving memory warning

I am developing an application with iOS 9 based SDK , this is my first time I am working with Storyboards , I have 20 view controllers, each scene has Next / Previous buttons to go back and forward . I have a huge problem with going forward !. If I move from scene 1 to for example to scene 15 I received memory warning and then application crashes . I have searched and it seems there is method called unwind segue but it seems this is for going back ! it's something like dissMiss method .
I connect each scene with line in Interface Builder :
Here is segue's setting :
I would be grateful if you help me out .
EDITED :
I tried to present a view controller programmatically but result was the same ! .
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
WhatIsDino *vc = (WhatIsDino*)[mainStoryboard instantiateViewControllerWithIdentifier:#"WID"];
[self presentViewController:vc animated:YES completion:nil];
Seems like it's a problem of wrong approach, and not the storyboard.
Let me guess, since before storyboard you used to change your app's rootViewController to the next/previous screen once you tap on the arrow button. So previous screen are released and deallocated from memory once you set a new rootViewController.
And now you're presenting every next view controller modally, which involved creating new UIWindow and loads all the hierarchy of you screen and keeps previous underneath the new one so it holds the memory and you're getting out of memory crash.
Well, you can do rootViewController approach with a storyboard too since it's just another way to manage your screens while development. Storyboard offers additional features like segues, static table view cells, general tint color and so on. [UIStoryboard -instantiateViewControllerWithIdentifier:] is the method you might find interesting.
But I'd rather recommend you to check out the UIPageViewController, it's like a container for the screens. Unfortunately, it cannot have the segues to your scenes (because of the special way segues work) so you have to use -instantiateViewControllerWithIdentifier: method anyway. You can treat inner view controllers of UIPageViewController as you do with rootViewController before.
You can also navigate without segue and Its easy way I think.
If you want to navigate from Class1 to Class 2 then follow these steps.
1) In Class 1, Import Class2.
2) In your button Action, Write this code.
Class2 *next = [self.storyboard instantiateViewControllerWithIdentifier:#"Class2 Identifier name"];
[self.navigationController pushViewController:next animated:YES];
Do not forget to give Identifier name in story board that is "Storyboard ID" in Attribute inspector of particular class.
No need to add Segue,Your storyboard would look clean.
The problem is that you are adding view controller after view controller with modal presentation. That causes each view controller to be added on top of the previous one, and all of them accumulate, using more and more memory.
Using a navigation controller and a push also piles the view controllers on top of each other.
You will have this problem if you use storyboards, nibs, or create the view controllers manually.
If you have a design where the user can move through a large series of view controllers then you probably want to dismiss the previous one before pushing/presenting a new one.
You can probably dismiss the previous view controller without animation and then present the new view controller each time you want to display a new one and avoid the memory issue. i'd have to experiment with it to get the effect I was after, but that's what I would suggest.

ios view controllers keep staking in memory

I am new to Objective C. I am working on my first app.
- It basically consists of 2 view controllers, and I use modal segue to switch between them. The main vc is a menu that loads up the 2nd vc with different attributes for each menu item.
- I noticed that the memory keeps increasing when I switch from one vc to the other. This was my attempt to solve the issue but it doesn't make a difference and it doesn't look clean.
-(void)viewDidDisappear:(BOOL)animated{
ViewController *me = self;
me = nil;
}
What is the best practice to handle memory in a case like this?
The problem is that you are using a modal segue in both directions. Don't do that. You are simply creating a new view controller each time: you have view controllers piling up on top of each other. The opposite of a modal segue (which is actually presentViewController:animated:, after all) is not another modal segue; it is dismissViewControllerAnimated: (or, with some added complexity, an unwind segue).

correcting navigation between VCs in existing app

I have a fairly simple app thats a game for small children. There is a main screen and 5 separate levels. 3 of the 5 levels are made up of more than one VC where actions take place in the first VC in that row then code calls a modal segue to the next one in the line and so on till it reaches the end of the row and a modal segue is called linking back to the main screen. The levels that have only one VC just perform actions then segue back to the main VC.
Every segue in the app in modal.
Also every page (VC) has a home button that will segue to the main page if pressed
I set this all up in the StoryBoard and visually everything works as Id expect but when adding sound I realized that there seems to be a major problem.
If I now understand correctly (and maybe I dont) modal segues dont actually replace the current VC with the newly requested one but rather slide the newly requested one over top the original and make it the visible display.
Currently I go from main to level 1. Level 1 does some stuff and plays some sounds that repeat via a timer. If I segue back to main visually everything is fine except the sounds being played by the timers in level 1 VC continue to play and xCdoe give me the following error quite a few times
2013-01-21 22:16:07.901 TTBetaDev[678:c07] Warning: Attempt to present <MainMenuViewController: 0x7e02f40> on <BonusViewController: 0x7ecbfa0> whose view is not in the window hierarchy!
Below is a screenshot of my storyboard in case I havent explained the layout well enough.
How should this be set up to allow the navigation I would like? A what steps will I need to take to apply that to the what I already have built in the storyboards? Or will I have to re-do all my storyborad work?
I tried apples VC documentation but I couldnt understand what relates to what Im trying to do.
COuld someone please help explain this to me
You have segues going forwards AND backwards. You shouldn't do this.
e.g. Look and Main and VC 2.
You have a segue going from Main to VC 2. This means that Main will present VC 2 as a modal view controller.
When Main does this though it is still on the stack underneath VC2.
Then you have a segue from VC2 to Main. This means that VC2 will create a new Main and present it modally too. If you continue using the app you will have multiple instances of main and all the other VCs and memory consumption will rocket.
What you need to do is delete ALL the segues that go backwards. (i.e. like the one from VC2 to Main)
Then when you want to get back to main from VC2 you have to dismiss VC2.
i.e.
in Main...
//present VC2
[self performSegueWithIdentifier:#"VC2Segue" sender:nil];
//dismiss VC2
[self dismissViewController:vc2ViewControllerInstance];
or in VC2...
//dismiss VC2 from itself
[self dismissViewController:self];
The main thing though is that you can't use segues to go backwards.
TL:DR
Nothing should segue INTO Main. Any segues that go into the left hand side of main should be deleted and dealt with properly.

Do modal segues create new objects?

Im transitioning from one view controller to another UINavigationController by using a modal segue. Its important for me that this view controller (and its child view controllers) stay in memory so specific references are kept up. Although obviously exactly this not happening. When debugging the viewWillAppear function the rootViewController (viewControllers[0]) reference points to different memory addresses between calls (and contains nil values, my actual problem).
Now there two possibilities which could cause this issue:
The UiNavigationController became destroyed
The rootViewController became destroyed
But to make it really confusing, none of them did happen; neither the UINavigationController nor the rootViewController became destroyed (viewDidUnload not called!).
Edit: Further investigation discovered that the UINavigationController is really recreated for every modal segue. I hope that by maintaining a property i can solve the problem.
I finally ended up by creating my own IBAction functions wich present the controller manually. This works just fine and is coded in less than 5 minutes. One just need to init the controller one time on ViewDidLoad from the storyboard.
Create a strong reference in the main view controller and point your new view controllers to that property. This will keep the view around as long as you need, although this is not recommended for n number of views because it defeats the purpose of a nav controller handling its own creation and removing of views.

Resources