Swift: Memory Not Clearing When I Segue to Another View Controller, Recieving Memory Warning

Swift: Memory not clearing when I segue to another view controller, recieving memory warning

It's hard to tell without seeing the code you're using to perform these segues. But I'm guessing that the problem has to do with how you are segueing between the view controllers.

I also have a "menu" button which performs a show segue back to the main menu.

If I had to guess based on your language above, instead of popping the Level ViewController (I'm assuming it is embedded in a UINavigationController), you are trying to segue back to the main menu using performSegueWithIdentifier: This will actually create a new instance of the view controller and push it onto the navigation stack (and retain the existing instance of it leading to your memory woes).

If that's indeed your problem, the solution is pretty simple: When your menu button is tapped, you should be calling popViewControllerAnimated or dismissViewControllerAnimated:completion:.

A simple little thing worth doing while you get accustomed to iOS segueing is to add de-initializers to all your view controllers as follows so as to get ongoing debug messages that objects are being deallocated as expected:

deinit {
debugPrintln("Name_of_view_controlled deinitialized...")
}

Happy St. Paddy's Day!

Segues and clearing historical ViewControllers from memory

When you are using segues the flow moves backwards and forwards. When the user moves backwards (ie presses "back") then it will not push to a new VC but it will pop to a VC that already existed. When you pop, the current VC is removed from the stack and memory.

If you have segues to move backwards in the flow then this is wrong. You only need segues to move forward.

A PROPER PREPARE FOR SEGUE

In prepare for segue you should never create your own view controllers and push to them. The storyboard is there to do all of this for you.

A proper prepareForSegue method should look something like this...

- (void)prepareForSegue:(UIStoryBoardSegue*)segue
{
if([segue.identifier isEqualToString:"SomeSegue"])
{
MyNewViewController *controller = segue.destinationViewController;

controller.someProperty = "some value to pass in";
}
}

That is all you need. Note that you only need this if you intend to pass some information to the new view controller. If you are not passing anything forward then you don't need this method at all.

When the method ends the new VC will get pushed onto the screen by the storyboard file.

UNWIND SEGUES

If you have a random flow (like in your comment) then you can use unwind segues to achieve this.

In you 'A' view controller have a function like...

- (IBAction)someUnwindAction:(UIStoryboardSegue*)sender
{
//some action to run when unwinding.
}

It needs to receive a UIStoryboardSegue object. If set up as an IBAction you can also access it from Interface Builder.

Now when you want to go A > B > C > B > A then just used the standard push and pop (from the segue and back button).

When you want to go A > B > C > A then you can use the unwind segue from controller C.

If you have a cancel button or something in controller C and this is in the Interface Builder and this should take you back to controller A. Then in the Interface Builder underneath controller C you will have a little green square with a door and an arrow pointing out of it. Just point the action of the cancel button to this symbol and choose "someUnwindAction". (Note, unwindAction is in A, button is in C.) XCode then uses this to pop you all the way back to A and deals with removing any memory and stuff. If you want you can send additional information back to A too.

If you want to access this unwind segue from C programmatically then you can run...

[self performSegueWithIdentifier:"someUnwindAction" sender:nil];

This will also pop back to A.

iOS10: How to avoid memory leaks in a segue circle

Presenting/Dismissing

NOTE:

If you are working with a navigation controller, you might want to check my answer about Pushing/Popping.


The action of "Back To Page1" in the fourth View Controller-, should be similar to:

@IBAction func backToPage01Tapped(_ sender: Any) {
presentingViewController?.presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil)
}

Note that the reason of why the number of presentingViewController are three, because it's the number of previous View Controllers for the fourth one.

If you have only two previous View Controllers, then you must chain backwards twice and call dismiss from two View Controllers back:

presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil)

and so on...

Attempt to present View Controller Warning

Don't present a view controller from viewDidLoad, instead call from viewDidAppear:. Also using dispatch_after like that (assuming you are using it to hopefully make sure the view is on screen and not for gaming purposes) is a very bad practice.

When the view controller that is being loaded has done being presented (that happens when viewDidAppear: is called) you can present a different one:

- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
GameViewController *gvc = [self.storyboard instantiateViewControllerWithIdentifier:@"gameViewController"];
[self presentViewController:gvc animated:NO completion:nil];
}

Changing the root view controller is not deallocating the previous view controllers on the same window

I think the rootViewController setting is not the problem. Perhaps you have a retain cycle in your other view controllers that stops them from being deallocated.

There are many ways you could accidentally do this (capturing strong references to self in blocks, not marking delegates or other back references as weak, etc).

You might be able to figure it out with Instruments. Here's a tutorial: http://samwize.com/2016/05/30/finding-retain-cycle-with-instruments/

ViewDidLoad calling again after segue performed

When you call performSegueWithIdentifier it will load a new view controller associated with "beginInduction" identifier and not go back to the existing instance which you may have created with a pervious performSegueWithIdentifier. What you have to do is to use an unwind segue.

More details are in this link.

https://developer.apple.com/library/ios/technotes/tn2298/_index.html

Alternatively you can use popToRootViewControllerAnimated or popToViewController or popViewController or dismissViewController methods to go back to the pervious view controllers

Feeling A Bad Design Layout Using Segues

From what I understand, by the time I get to D I now have instances of A B C and D open, which does not sound good

A view controller, of itself, is a fairly lightweight object, and there is no problem whatever with going many levels deep (e.g. pushing five view controllers onto a navigation controller stack). However, memory and images you may be holding on to are not lightweight, so be sure to implement didReceiveMemoryWarning and take it seriously if it arrives.

A strategy for letting go of large retained memory-hogging stuff in response to didReceiveMemoryWarning is to save it off to disk (if it can't be recreated on demand) and then use lazy initialization to read it back in the next time you are asked for it.

Size of memory usage of IOS App

a) is this a lot of memory to be using

lot is a relative term and whether 530Mb is lot or not completely depends on what app is doing and what kind of app it is! If its a game with rich graphics I wouldn't be bothered much but if its a plain utility app I would be concerned!

b) should I be worried

Memory getting accumulated/increasing as user loads various screens of app is a very common scenario. You should be bothered if it keeps increasing and never comes down. Every time a VC loads it might load, heavy graphics with it or might allocate large amount of variables and consume lots of memory to perform its function. Which is quite fair. But once user pops the VC from applications navigation stack all the memory allocated by the VC should be returned and total memory consumed by the app should come down. Ideal memory foot print would look like a wave where it reaches peak and comes down once VC pops out. If thats not happening you are in trouble :)

c) how can I remove view controllers from memory as I navigate round

  1. Use proper navigation techniques. Don't keep pushing the VC's to navigation controller stack unless you actually need that VC instance to be retained in memory. All VC's pushed to Navigation stack will continue to be kept in the memory till either user kills the app or iOS decides to kill the app on receiving memory warnings.

  2. Write deinit/dealloc in each VC and make sure it gets called every time user pops the VC by either tapping back button if its pushed or by dismissing VC if its presented modally. Ensuring each VC's deinit gets called is the best way to ensure VC does not hold up any unnecessary memory.

  3. Make sure none of your VC has code which results in retain cycle and retains the objects in memory forever. Example : If your VC declares a block and holds the strong reference to block and if you pass self to block your block and self will never be released. Classic example of retain cycle. Make sure your code does not create such dead locks

  4. Never hold anything strongly by using either strong/retain unless its necessary.

  5. Use instrument to find the memory leaks and reference counts of each objects just to make sure there is no memory leaks in ur app.



Related Topics



Leave a reply



Submit