I have a root view that loads two view controller. e.g.:FirstVC,SecondVC.
I am showing FirstVC as the root view controller when the app launches, on some action on FirstVC I load SecondVC by removing first.
For loading SecondVC I first remove FirstVC by
[FirstVCobj.view removeFromSuperView];
[FirstVCobj release];
FirstVCobj = nil;
After that I allocate and create SecondVC
Now only after calling SecondVC's viewdidload() is FirstVC's dealloc() method called.
Is this the right execution path, or is it due to some mistake I have made?
The above is exactly how I remove and create my view controllers.
i assume it is a UIView you're talking about.
addSubview retains the view
removeFromSuperView releases or AUTORELEASES it -- an implementation detail you don't control
to 'see' it: wrap it in a pool of your own
#autoreleasepool {
[FirstVCobj.view removeFromSuperView];
[FirstVCobj release];
FirstVCobj = nil;
}
[FirstVCobj removeFromParentAndCleanup:YES];
Check with this might work.
Related
I am little confuse because my dealloc is not called in ARC. I have using storyboard in my application.
Case 1: Mydealloc called when i use all IBOutlet from storyboard
Case 2: My dealloc is not called when i try to use alloc and init methods in UIViewController. such as below.
UIViewController *vc = [[UIViewController alloc] initWithNibName:#"ProfileDetailView" bundle:nil];
__weak ProfileDetailView *detailview = (ProfileDetailView *)vc.view;
detailview.backgroundColor = [UIColor clearColor];
vc = nil;
....Set value in object.....
[self.view addSubview:detailview];;
detailview = nil;
Can you explain why dealloc is not called? and How can i able to achieve to call dealloc?
Thanks
The concept of ARC is that an object's retain count should theoretically be 1 in order for it to be deallocated. When you execute:
[self.view addSubview:detailview];;
Your self.view increments detailview's retain count by 1 when it adds it to view.subviews. Logically, when self.view removes detailview, the retain count is decremented by 1.
Again, this is a theoretical perspective. The reality is usually:
INSANE.
No one really knows how the mysterious Objective-C runtime works! (just kidding the whole source code is available online.)
Thanks for your reply. I am able to called my dealloc function when view controller pop. For achieving that, we need to remove my added subviews from my view when user tapped on back button.
I have this view controller (class1) , which has a UICollectionView in it .
When i am finish with this view, i am going to the next view, but i can see that the memory consumption of this view(class1) is not cleared and being added to the next view (class2).
Both view controllers are made with storyboard, and has a name , and when i finish with view1 (class1) i am going to the next one with :
//in view1 i do when exit
UIViewController *mainV=[self.storyboard instantiateViewControllerWithIdentifier:#"MainView"];
mainV.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentViewController:mainV animated:YES completion:^(void)
{
[self.myCache removeAllObjects];//NSCache
[self.GridView removeFromSuperview]; //collection view
[self.view removeFromSuperview];
}];
Seems that memory is still not freed.
Is there another way to move to next view and just clear everything before ?
In that code class1 is presenting class2. But class1 is still there, presenting class2, until it does a "dismissViewController:" method.
If you want to go back to mainView from class1. And in the case of class1 being presented by mainView. Then mainView have to do a dismissViewController:
In other case you are stacking a pile of view controllers, one presenting the next one.
The pattern most used in objetive-c is to send a message from class1 to its presenter. A delegation pattern. The presenter then dismiss the viewController.
Even after grid view is removed from superview, it won't be cleared from memory if it's an iVar of your view controller class or a strong property. Try setting GridView to nil after you call [self.GridView removeFromSuperview].
In an application where several UIViewControllers work together,
firstViewController added to root. Till here its fine now I want to go to secondViewController I dont want to use UINavigationController or UITabBarController. I have already read the View Controller Programming Guide but it does not specify without using UINavigationController, UITabBarController and story board.
and when user want to move from secondViewController to firstViewController how secondViewController will be destroyed?
Apple Doc also does not specify how UIViewController is release or destroyed? It only tell the life cycle inside UIViewController.
If you are concerned about how UIViewController is release or destroyed then here is a scenario for you:-
Here is a button tap method in FirstViewController that presents SecondViewController (using pushViewController,presentModalViewController etc)
In FirstViewController.m file
- (IBAction)btnTapped {
SecondViewController * secondView = [[SecondViewController alloc]initWithNibName:#"SecondViewController" bundle:nil];
NSLog(#"Before Present Retain Count:%d",[secondView retainCount]);
[self presentModalViewController:secondView animated:YES];
NSLog(#"After Present Retain Count:%d",[secondView retainCount]);
[secondView release]; //not releasing here is memory leak(Use build and analyze)
}
Now In SecondViewController.m file
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"View Load Retain Count %d",[self retainCount]);
}
- (void)dealloc {
[super dealloc];
NSLog(#"View Dealloc Retain Count %d",[self retainCount]);
}
After Running the code:
Before Push Retain Count:1
View Load Retain Count 3
After Push Retain Count:4
View Dealloc Retain Count 1
If you are allocating and initializing a ViewController, You are the owner of its lifecycle and you have to release it after push or modalPresent.
In the above output at the time of alloc init retain count of SecondViewController is One,,,,surprisingly but logically its retain count remains One even after it has been deallocated (see dealloc method), So require a release in FirstViewController to completely destroy it.
Other way to present a new view controller is doing it like a modal view controller(notice that self is firstViewController):
[self presentModalViewController:secondViewController animated:YES];
then, when you wan to come back to the firstViewController and destroy the secondViewController, you have to dismiss the view controller(from the secondViewController):
[self dismissModalViewControllerAnimated:YES];
Hope that helps.
You can use UINavigationController to move to secondViewController and come back by setting the UINavigationController property 'navigationBarHidden' to YES. Which will hide the navigation bar. Releasing and destroying of view controllers will takes care by this.
Then, you can take other strategy, is not the best to build your view controller hierarchy, but it also could work. You can overlay the secondViewContrller's view over the firstViewController's and make the secondViewController become the child from the firstViewController:
//...
[self addChildViewController:secondViewController];
[self.view addSubview:secondViewContrller.view];
//...
And when you want to remove the view controller, you have to remove the view and ask for the view controller to remove from his parent:
//...
[self.view removeFromSuperview];
[self removeFromParentViewController];
//...
But then you will have to control the view hierarchy by your own(putting and removing views and view controllers by your own).
I am working with Automatic Reference Counting.
I have a custom UIViewController subclass and whenever I call -presentViewController: animated:completion: or remove its view from the superview I would like to NSLog something like "I am dealloced" so I know that the view controller has successfully been removed. I have implemented the -dealloc method in my view controller. However I started a test project where I just had two UIViewController instances (no retain cycles) and -dealloc is not called either when I push the second UIViewController modally or when I remove the superview or when I remove it from the parent view controller. Am I missing something ? In my original project (not the test case) Instruments shows me that those controllers leave a memory footprint that I can't get rid off.
If you want to switch view controllers, and have the one you're switching away from be deallocated, then just switch the root view controller of the window. So, if you're in VC1 and want to go to VC2, then do this in VC1:
VC2 *vc2 = [[VC2 alloc] init]; // or however else is appropriate to get an instance of this class
self.view.window.rootViewController = vc2;
If you haven't created any property to point to vc1, then it will be deallocated after making this switch.
If you want to use a modal presentation or a modal segue (to get the animation when you switch controllers), you can still get the initial controller to be deallocated by switching the root view controller after the presentation from the viewDidAppear method of vc2:
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.view.window.rootViewController = self;
}
To get a print when the View Controller is deallocated you can implement the dealloc method as
- (void) dealloc {
NSLog(#"The instance of MyViewController was deallocated");
}
Then to get a print when the View Controller left the view you can implement
- (void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
NSLog(#"The instance of MyViewController left the main view")
}
If you use -presentViewController:animated:completion: you are retaining the parentViewController every time you call this method. ModalViewControllers are simply pushed on top of the other ViewController.
ModalViewControllers are only designed for some kind of information / User Input and stuff like that. If you want to dealloc the ParentViewController you have to deal with your own implementation.
dealloc method isn't called when the class is retained (or something in this class is retained) and not reeleased. It is justly for projects with both ARC and without it. So check your code twice.
I'm displaying UIControllerView subclass when a button is pressed from a another UIViewController like this:
- (IBAction)openNextLevelViewController
{
NSLog(#"openNextlevelViewController");
[self.navigationController pushViewController:nextLevelViewController animated:YES];
}
And the app will return from that view on a button push which trigger this method:
-(IBAction) returnToStart {
NSLog(#"returnToStart method called");
[self.navigationController popViewControllerAnimated:YES];
}
The problem is that the displayed view not getting destroyed/deallocated on the pop. As a result, when it gets pushed, it's not executing the viewDidLoad, which initiates some variables. This may be causing a related problem where, the second time through, when the user presses the return button, the "pop" no longer causes a return to the previous controller.
What's the best way to deal with this? I could move the initialization code to the "willAppear" method, but it seems as if that could be called almost randomly.
Well, its not getting released because nextLevelViewController is still being retained somewhere else. Most likely in your nextLevelViewController variable.
- (IBAction)openNextLevelViewController
{
NSLog(#"openNextlevelViewController");
// assuming you have nib already set up
UIViewController *nextLevelViewController = [[NextLevelViewController alloc] init];
// RETAIN COUNT = 1
// navigationController retains your controller
[self.navigationController pushViewController:nextLevelViewController animated:YES];
// RETAIN COUNT = 2
// Don't need it any more let navigation controller handle it.
[nextLevelViewController release]
// RETAIN COUNT = 1 (by NavigationController)
}
Further On
-(IBAction) returnToStart {
[self.navigationController popViewControllerAnimated:YES];
// RETAIN COUNT = 0, dealloc will be called on your viewController, make sure to release all your retained objects.
}
Now when your controller gets popped, it SHOULD get released (shouldn't have been retained anywhere else). And next time you call openNExtLevelViewController, it'll be initializing a new instance of your viewController anyway.
I'm a fan of releasing viewController when it is no longer needed (displayed), instead of holding it in memory. Let navigationController and TabBarController handle viewControllers whenever possible.