Navigation Controller and Bar - Memory Managment - iphone

first of all... i'm italian, so i'm sorry for my bad english!
so... this is my app:
i'm using a navigation controller: in the first view there are 10 buttons and every button calls a functions like this:
[self pushViewController:nameview animated:YES];
to a different uiviewcontroller! So i have 11 uiviewcontroller!
Every controller is decleared like here
#interface ...
IBoutlet UIViewController *viewcontroller;
...
#property (nonatomic, retain) IBOutlet UIViewController *viewcontroller;
Finally i have to say that i'm working with IB!
My problem is that my app doesn't release memory!
when i'm in a view and i tap on the "backbuttonitem" (created by IB, not by me) the last view doesn't became released (again, sorry for my bad english)... and if an user see all 10 view, the app receive a warning massage (low-memory)!
How can i release the last view saw before the popviewcontroller action?
thanks :D

When you push a view controller onto a navigation controller you need to release it as the navigation controller now owns it. For example you would do the following:
UIViewController *controller = [[UIViewController] initWithNibName:#"Nib" bundle:nil];
[self pushViewController:controller animated:YES];
[controller release];
Then when you use popViewControllerAnimated: the navigation controller will take care of making sure the view controller is released from memory.

Related

Crash when instantiating ViewController from Storyboard

I have got a single view in my storyboard, which I add to my current view by doing the following :
MainViewController *mvc = [self.storyboard instantiateViewControllerWithIdentifier:#"MainController"];
[self.view addSubview:mvc.view];
The view appears, but everything I do after it appears, leads to a crash. What am I doing wrong ?
Here is an example when it crashes:
-(IBAction)showUsername:(id)sender{
[testLabel setText:#"username"];
}
Everything is hooked up in storyboard as well, so falsely linked connections should not cause the problem.
You instantiate a new view controller:
MainViewController *mvc = [self.storyboard instantiateViewControllerWithIdentifier:#"MainController"];
But you do not retain it. Your view hierarchy is, as soon you added it to another view.
[self.view addSubview:mvc.view];
So when a button is clicked, a message is sent to you IBAction, but your view controller has been released already. To prevent this from happening, retain your mvc variable, for example somewhere in a property.
#property(nonatomic, strong) MainViewController *controller;
self.controller = mvc;
I can think all reason before you show log...
Turn NSZombie on in the Product>>Edit Scheme you should get more descriptive Error showing then. Then you can add it.
Make sure your method is declared and implemented correctly. Also make sure you have IBOutlet UILabel * testLabel in your .h. The only other problem I can think of other than that is how you hooked it up. Does it only crash when you press the button?
This line is wrong this will be why you are getting the error.
MainViewController *mvc = [self.storyboard instantiateViewControllerWithIdentifier:#"MainController"];
[self.view addSubview:mvc.view];
replace it with this
MainViewController *mvc = [self.storyboard instantiateViewControllerWithIdentifier:#"MainController"];
[self presentModalViewController:mvc animated:YES];
In storyboards you are not adding a subview you are doing one of three things presenting a modal, pushing it on to the navigation controller stack or making a custom one of these.

ViewController never gets deallocated

In my mind, myViewController should be deallocated around the time that I pop back to the root view controller with the following code, but I never see the deallocation message getting NSLogged.
If this should work, then what kind of problem can I look for in the myViewController's class that might cause it to get deallocated when I popToRootViewController?
Thanks.
The following gets called in my tableView:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
MyViewController *vc = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:nil];
[self.navigationController pushViewController:vc animated:YES];
[vc release];
}
UPDATE:
This code was perfect, but it was some bad memory management in my custom view controllers that caused neither to be released. I had some retained properties that should have been assign instead (or at least, that's the way I solved it). See comments for specifics.
When you use poptoviewcontroller then dealloc method will call for the topmost view controller in navigation controller. You can put a breakpoint in dealloc method of your current view controller and when you called popviewcontroller then your dealloc method gets called and release all the stuff/varaibles you have created in your view controller.
#JaySmith02 is right
it was some bad memory management in my custom view controllers that caused neither to be released. I had some retained properties that should have been assign instead (or at least, that's the way I solved it)
In my case the culprit was
#property (nonatomic, retain) id<TTSlidingPagesDataSource> dataSource;
From my viewController when I wrote
slidingPages.dataSource = self
I guess the dataSource retained my viewController and made a circular retention. The 'dealloc' of my viewController was never getting called.
Note: Under ARC dealloc does get called. Difference is you cannot call [super dealloc]
The solution:
#property (nonatomic, assign) id<TTSlidingPagesDataSource> dataSource;

Question about the mechanics of iPhone view controllers (i.e., explain why this crashes)

I am pretty new to iPhone programming, and was playing around with an app yesterday trying different scenarios with view controllers and nib files. So, I started a new app with a FirstViewController (FVC for short) and an FVC.xib.
I layed out a quick view in FVC.xib and ran the app - view displays, great.
I now wanted to have a second view I could add on top of the main view. So I went ahead and created SecondViewController.xib (SVC) but did not create the .m and .h files. I went about trying to load both these views from the same view controller, and here is where my question lies:
I created a button in FVC.xib and created an IBAction like this:
- (IBAction)loadSVC {
FirstViewController *viewController = [[FirstViewController alloc] initWithNibName:#"SecondViewController" bundle:[NSBundle mainBundle]];
secondView = viewcontroller.view;
[viewController release];
[self.view addSubView:secondView];
}
So this works great and adds the contents of SVC.xib, but when I try and remove that view from the superview, the app crashes:
[secondView removeFromSuperview];
If I actually create a view controller for SVC, use that to instantiate my view in FVC, and move the remove code to the SVC:
[self.view removeFromSuperview];
Everything works. My question - I kind of get why my first method crashes, but I was hoping someone could explain why and what goes on behind the scenes. I'm still a noob with object oriented programming, so what is actually happening in my first case where I create a new instance of FirstViewController and add its view to self.view? Why can't I release it (I assume because the original view is associated with FirstViewController, and when I create a new instance with the second xib it messes everything up) - I'd love a more technical explanation as to what is happening...
Thanks much!!
EDIT to add more info in response to Nick's reply below
Nick - so your answer did clear my thinking a bit in regards to the retain count, etc... I did another test app trying to get this working from a single view controller - think, for example, that I wanted to display an Alert or Welcome message to the user (I know in a real app there are different methods to accomplish this, but this is more of a learning experience) -- so I have my main view # MainViewController and layout my alert message in a xib called alert.xib -- so there is no logic behind the alert message, no reason for it to have a view controller that I can see, my end goal being loading/unloading this on top of my main view from the main view's view controller (or understanding why it is impossible)
I tried this using instance variables as you recommended:
In MainViewController.h:
#import <UIKit/UIKit.h>
UIViewController *secondController;
UIView *secondView;
#interface MainViewController : UIViewController {
}
#property(nonatomic, retain) UIViewController *secondController;
#property(nonatomic, retain) UIView *secondView;
- (IBAction)loadSecond;
- (IBAction)removeSecond;
#end
In MainViewController.m:
#import "MainViewController.h"
#implementation MainViewController
#synthesize secondController, secondView;
- (IBAction)loadSecond {
secondController = [[MainViewController alloc] initWithNibName:#"alert" bundle:[NSBundle mainBundle]];
secondView = secondController.view;
[self.view addSubview:secondView];
}
- (IBAction)removeSecond {
//I've tried a number of things here, like [secondView removeFromSuperview];, [self.secondView removeFromSuperview];, [secondController.view removeFromSuperview];
}
- (void)dealloc {
[secondController release];
[secondView release];
[super dealloc];
}
So - this works to load the alert view, but the removeSecond button does nothing (I did use NSLog to verify the removeSecond method is fired) - why?
Second, and most importantly - is this even possible, or is it horrible practice? Should every nib/view I am manipulating have their own view controller? Am I wrong to think I could just make a new instance of MainViewController and use it to display and remove this no-functionality, very temporary view? (And yes, I realize I could easily create this view programatically or accomplish the end goal in many different ways which would be easier, but I'm trying to really learn this stuff and I think figuring this out will help...
Thanks for the help!
You created a view controller
You accessed its view which caused controller to create the view and call the delegates (i.e. viewDidLoad)
Controller returns the view that you asked for
Now you add the view as a subview which increases its retain count
Controller is released and it releases the view, BUT since view's retain count was increased the view is still there
You try to remove the view, it is unloaded and delegates are to be called (e.g. viewDidUnload), however that messes up since the controller who created the view is released and that piece of memory is... smth else :)
That's why the first method doesn't work.
The second method is NOT correct either but it works because:
You remove controller's view from superview but since controller itself is not released (you didn't call [self release] or anything like that, not saying that you should :), just an example), then the view didn't reach 0 (zero) retain count and is still there - which means its subviews aren't removed
The proper way to do it is to save the reference to the controller as an instance variable (usually declare a synthesized property), and release it only when you are done with the view, making sure that the view is removed from superview before hand. The default templete for a View Based App shows how view controller should be managed
Hope this helps to understand why both methods behave differently
Based on your clarifications, you don't need secondView property or iVar. Also in your loadSecond instead of secontController = bla you need self.secondController = bla, otherwise you simply assign reference to the iVar instead of going through the setter.
Yes, it's possible to load subviews/other resources from a nib without having a dedicated controller
This is how you do it (one of the approaches):
UIView *result = nil;
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:#"MyNibName" owner:owner options:nil];
for ( id o in bundle ) {
if ( [o isKindOfClass:[UIView class]] ) {
result = (UIView *)o;
break;
}
}
Here the result will contain the first UIView in MyNibName. You can use other criteria to find out whether you got the view you wanted (tags, types...)

Is it Possible to have two view Controller's IBOutlet in one .h file? How to move to previous to previous view?

I am using XCode 4.
In My applicaion
I have Files
WelcomeViewController
FirstViewController
SecondViewController
ThirdViewController
In every view I have same functionality.
Pressing the Button and Load another view By presentModelViewController..
Problem I am Facing is I can not Declare Two outlets in One header file As If I want to move to the Back Page.?
#import <UIKit/UIKit.h>
#import "ThirdViewController.h"
#import "FirstViewController.h"
#interface SecondViewController : UIViewController {
UIButton *button1;
}
#property (nonatomic,retain) IBOutlet ThirdViewController *thirdVC;
#property (nonatomic,retain) IBOutlet FirstViewController *firstVC;
//Error at this line above
#property (nonatomic,retain) IBOutlet UIButton *button1;
#end
Is it a good practice to alloc and init the new controller or I just have to take a view controller in XIB file and then just create Outleyts???
Yes you are right definbately, I have tried it is working...but why I am taking View Controller because I just can jump to previous to previous view ? What is wrong If I take an IBOutlet.???
So, What if I want to move previous to previous view I need to write
two times
[self dismisviewContrlloer animated:YES];
[self dismisviewContrlloer animated:YES];
????
Why would you want to have 3 outlets? Just have properties for the viewcontroller. Alloc and init and present the views of the same controller class.
So basically you will have a xib file for a viewcontroller and you will need to create as many instances of that depending on your requirement. Looking at the code i think it needs lot of redesign. You present and dismiss these views accordingly.
Actually, all you need to do is [self dismissModalViewControllerAnimated:YES]; If the 3rd view is visible, the 2nd one will automatically become visible. Similarly the 1st from the 2nd. You don't really need to store references to adjacent view controllers in each view controller.
HTH,
Akshay
[self.parentViewContrtoller.parentViewController dismissModalViewControllerAnimated:YES];
By this method I can Jump Back to the previous to previous move.

How should I load a xib into a detail view for a split view iPad objective app?

I've got a split view iPad app with table view in the master pane. After drilling down several levels, each time loading a new .xib, I'd like one of the cells to trigger a web page load in the detail pane. Right now I can only get the web page .xib to load in the master pane side -- which is a master pain in my side.
The basic load call where "URLWindow" is a class loaded with initWithNibName:
[[self navigationController] pushViewController:URLWindow animated:YES];
I want to do this, but it doesn't seem to work:
#interface
#property (nonatomic, retain) IBOutlet DetailViewController *detailViewController;
...
#implementation
[[self detailViewController] pushViewController:URLWindow animated:YES];
How should I be loading the URLWindow .xib into a detail view for a split view detail pane?
couple of things to check:
1) you may try setting BOTH master / detail controllers as 'UINavigationControllers' ?
2) [detailViewController.navigationController pushViewController:URLWindow animated:YES];
you are calling pushViewController on 'detailViewController', this is calling it on 'navigationController'