I have a LoginViewController (UIViewController) that when all the criteria is met and the user hits the Login button, a storyboard segue is run that pushes the ProfileViewController (UIViewController). When this happens, I have a log statement in my LoginViewController's dealloc method to see if it is called and to my disappointment it is never called. My question is whether or not it is supposed to be called? Also, when I log in, sometimes I get a "Received memory warning" and sometimes I do not which I find strange because I am taking the exact same steps in both cases and yet i get a memory warning one time and not with the other.
Anyone can shine some light on this that would be great!
Thanks.
UINavigationController maintains a stack of view controllers. You start with one element, a LoginViewController, on that stack. When you push a ProfileViewController, you now have two elements on the stack. The LoginViewController can't be deallocated until it is removed from the stack.
If you want the ProfileViewController to replace the LoginViewController on the navigation controller's stack, you can write a custom segue class to implement that behavior. See this Q&A.
(You might think you could use the “Replace” or “Show Detail (e.g. Replace)” segue type in your storyboard, but those only work if you are using a UISplitViewController.)
With ARC enabled, when a object is not referenced, it will be released.
In order to display view from ProfileViewController, you instantiate a object of it in LoginViewController, and that's how you still can see the profile view after it is presented. If LoginViewController instance is released, the profile view will also get released(assume no one else references it). For the same reason, the LoginViewController instance is not released because another object is holding a reference to it. Say your views are presented in Window -> ProfileViewController -> ProfileViewController, it's the window that keeps ProfileViewController instance from being released.
If you have two views as I assumed so far, the memory warning should be from somewhere else. Two views cannot cause the issue.
Related
In my app I have a RootViewController which all my ViewControllers are subclassed from. When developing, I usually use this in my RootVC:
deinit {
print("\(type(of: self)) deinit")
}
so that I always can see when any of my viewControllers deinit. It prints:
MyExampleViewController deinit
Today I noticed that one of them didn't deinit when I navigated away from it. Let's call it DetailViewController. It's a completely normal (Root)ViewController-subclass pushed into the main NavigationController. When hitting the Back button in the navigation, it navigates away, but never says it deinits. This is the first pushed controller, so I can't pop the controller before to see if that helps. But any controller pushed after the DetailViewController gets deinited fine when navigating back and forth.
I decided to check the memory graph, so I ran my app again, pushed to the DetailViewController, then popped it away by clicking the Back button in the navigation, then I clicked Debug memory graph.
In the debug navigator on the left, I scroll down and see that there exists one instance of my DetailViewController. If I push back and forth several times before opening the memory graph, there are as many different instances of this DetailViewController as times I've pushed and popped.
When clicking it, I see this:
The DetailViewController is the single controller on the far right. I haven't used Memory Graph that much, but I assume that the "solid" white lines are strong claims, and that the slightly more transparent (gray) lines are weak claims. Meaning that there's one strong claim to my controller. The one on the bottom.
This is the bottom row:
What does this mean? It seems like my (custom) NavigationController has an array called _childViewControllers which retains my popped controller. To clarify, I don't have any stored variables in my custom NavigationController. It's only subclassed to override 5 functions, that is all. I have about 20 different ViewControllers being pushed and popped by this exact same custom `NavigationController, but they all have no problem with this.
Am I reading the graph wrong? There has to be a different strong claim that's not visible in the graph, right? When I "pop" the viewController by clicking Back, shouldn't my viewController be removed by _childViewControllers?
Figured it out at last. Unfortunately, I had to go through commenting out several hundred lines of code bit by bit until I found out when it started to deinit when expected.
The issue was a missing [weak self] in a closure, not unexpected, but it was in a completely different class, connected through a complicated hierarchy.
I have two UIViewControllers, VC1 and VC2. VC1 has a button that invokes a triggered Modal segue to VC2. This segue is defined in the storyboard. In VC2, the user can return to VC1 using a pan gesture that executes this line of code:
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
This code has worked for a long time. However, all of a sudden when this line runs, the app crashes. I enabled zombie objects and I can see this error:
-[VC2 retain]: message sent to deallocated instance 0x7f843a81e200
I've run the zombie profiler and here are the results:
UIClassSwapper initWithCoder seems to be where the app crashes, and there appears to be a an over retained object there, but I can't get any further. When I set an exception breakpoint, I just get into the assembly code, and this isn't of any apparent help.
So, there are a couple of questions here. The first is simply what am I doing wrong in how I am presenting and dismissing this view controller? After that, I don't know how to crack into the event history to figure out how to handle this zombie object.
Any help is appreciated. Thank you!
It seems that you use ARC project. Please provide more code:
1. How you initialize presentingViewController property (and declare). Is it (nonatomic,strong)?
2. How do you put this ViewController to the screen. Is it a kind of modal view controller etc?
-- added after comment.
ARC based project decides automatically if the object is needed or not and removes it from the stack automatically. So, the only one useful recipe to avoid this problem is to add NSNotification observer and post there notifications time after time. It will keep view controller in the stack and avoid automatic release.
Another solution - check if the controller is still in the stack:
if (!self.presentingViewController) [[THECONTROLLER alloc] init];
The third one - is to use some object to declare VC1 and VC2 as class variables, and present them as modal view controller when needed. In this case, object and its variables will be kept untouched as long as any of VCs presented.
In any case, the Exception happens because ARC considers that VC is not needed anymore and releases it atomatically.
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.
I am new to iOS programming, i read the view controller programming guide but some things are still unclear to me
View of a viewcontroller is destroyed but viewcontroller remains, there nothing specified regarding if I my self want to destroy any viewcontroller or I just missed it. VC guide says iOS destroy VC when memory start getting short for other resources and same goes for VC's view right? Need to understand memory management of VC, please read my second point.
In a uinavigationcontroller i have a tableviewcontroller which display list of my friends, on selecting any of my friend i am pushing another VC of chat. The problem is every time on selecting a friend do i have to create a new instance of chat VC? how this chat VC will be unique for every friend i am trying to chat with? I am also saving messages in DB so who ever friend window open or close messages shows up, user don't get a blank view. Is it possible only one instant of chat VC is used for every friend uniquely(means only message of particular friend show and send to him to whom i am trying to chat). I think this problem is related to passing data between viewcontrollers but i am confused.
I hope i have clearly define what i am confused in. Please ignore any silly mistakes.
Thank you.
Answer to your queries -
View of a viewcontroller is destroyed but view controller remains
According to UIViewController guide -
It is responsible for creating those views and for relinquishing ownership of them at the appropriate times, including during low-memory conditions and when the view controller itself is released.
So we need to take the ownership to release a view controller.
Also The UIViewController class provides some automatic handling of low-memory conditions through its didReceiveMemoryWarning method, which releases unneeded memory.
In your Scenario-
TableView -> on click of cell push next view -> now pop this view to go back to table view(here memory got release) -> in table view again -> on click of cell a new view controller is pushed.
In this way it handles memory. If in a hierarchy we are pushing some views like -
VC1 -> VC2 > VC3 -> VC4
Now in this scenario navigation controller keeps instances of all these view controller, as navigation controller gives us flexibility of moving backward. And as soon as we go back that view controller is removed from stack and memory allocated to that is freed.
Syntax used -
Push a view controller -
[self.navigationController pushViewController:myViewController animated:YES];
[myViewController release];//at the time of push we add this release statement.
Now when we do a pop, its dealloc method is called and this is removed from the stack.
[self.navigationController popViewControllerAnimated:YES];//That view is release from navigation stack
Hope this clears your doubt.
Checkout this developer page for more details - http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ViewLoadingandUnloading/ViewLoadingandUnloading.html
I am new to iOS and wondering how to properly implement view controller unloading/reloading.
Right now my app has a NavigationController with a MainMenuViewController (custom view controller) set up as root view controller. During the course of app lifetime, new ViewControllers are pushed/popped on the Navigation Controller. This works fine, the appropriate ViewControllers are initiated (from NIBs) the first time they are pushed to the stack.
However, I now want to unload one particular ViewController when it is popped, then reload it automatically when it is pushed again.
I have added a [self release] to that ViewControllers viewDidDisappear: and it unloads, but when i try to push the view again, i get a message sent to dealloc'ed instance error and crash. Therefore, my questions are:
Is that a proper way to unload a popped ViewController?
How to check if a given ViewController is loaded or not?
How to force a reload? With loadWithNib:, then push onto navigation stack?
Regards,
Peter
Welcome to iOS programming. Your crash is a memory management issue. It may take you a bit to get the hang of it but memory management gets way easier if you just follow one rule:
an object needs to release anything it retains (alloc is equivalent to retain)
In this case, your view controller is releasing itself and it definitely did not retain itself. Here's how the sequence works with a navigation controller:
The navigation controller is initialized with a root view controller (the first one on its stack). Lets call this firstViewController
A user action tells firstViewController to initialize secondViewController and push it onto the navigation controller. In most cases, firstViewController will release the instance of secondViewController after pushing it. At this point, firstVC is done with secondVC. The navigation controller is now retaining secondVC
User touches the back button on the navigation bar of secondVC. The navigation controller will pop secondVC from the stack and release it. As long as no other object is retaining it, secondVC will get be dealloc'ed.
Now user is back in firstVC. They can do the same user action which will init and push a new instance of secondVC.
Hope that helps a bit.
I'd also recommend you (re)read the Apple docs and look at the sample code referenced in the framework docs.
http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/Introduction/Introduction.html