popViewControllerAnimated: doesn't release view controller from memory - ios

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).

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.

Managing instances of programmatically instantiated view controllers

If I instantiate a ViewController from my storyboard programatically, will its memory be freed once it's no longer be shown in the application?
I'm showing it as a modal.
Expanding on #Schemetrical's answer, you need to make sure there is at least one strong reference to your VC or it will be deallocated immediately.
This is a crash in the making:
func viewDidLoad()
{
childVC = self.storyboard.instantiateViewControllerWithIdentifier("childVC")
self.view.addSubview(childVC.view)
}
In the above example the current VC's content view keeps ownership of the newly created view, but nobody keeps ownership of the view controller. It gets deallocated as soon as the function returns, and the first time something tries to reference the now-deallocated VC, you crash (Say there is a button who's action points to the VC.)
If you push your VC onto the navigation stack, the navigation controller takes ownership. As soon as it's popped off the stack, it gets deallocated. If you present your VC modally, the system takes ownership for as long as it's on screen. As soon as it'd dismissed it gets deallocated.
If you want a VC to stick around after it's popped/dismissed, you need to keep a strong reference to it somewhere. You might save a reference to it in your app delegate, in a singleton, or in your app's root view controller.
As long as nothing holds strongly on the vc, it will dealloc. Once you dismiss that vc, the view releases its reference on the object and since there are no references, it will dealloc.

Does manually setting NavicationController.viewControllers release the existing viewcontrollers?

I have code that replaces an entire stack of ViewControllers in a NavigationController, as sort of a "reset" that switches between a number of different threads of UI scenes. Instead of creating a different NavigationController for each flow in storyboard, I decided to just change the NavigationController.viewControllers array manually:
self.navController.viewControllers = [vc1, vc2]
What happens to the old stack of ViewControllers? Do they automatically get release/unloaded? or do they hang around forever and therefore causing memory leak?
Note:
I know about unwind segue, but I didn't want to use that since even if I unwound to the first VC in NavVC, I still have to replace it with a different VC, and setting .viewController is the only way I know to not result in a "Back button"
What happens to the old stack of ViewControllers?
The viewControllers property points to an array and has copy semantics, so when you assign an array of view controllers to that property, the navigation controller copies the array. When you assign a different array to that property, the previous one will be deallocated (assuming there are no other strong references to it). The same is true for any of the view controllers in the array -- if nothing else has a strong reference to them, they will be deallocated. This is no different from any other Objective-C object.
After you set the new view controllers, your UINavigationController will lose its strong reference to the old stack. If you do not have any remaining strong references to the items in the old stack, they will indeed be released by ARC.
One way to verify this behavior is to override -(void)dealloc in the view controllers you want released, and put some logging in there. you should be able to see when dealloc is called.

Why ARC is not deallocating memory after popViewController

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.

popToViewController and memory management

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.

Resources