I always push a new view controller onto the stack like this:
MyViewController *vc = [[MyViewController alloc] init];
[self.navigationController pushViewController:vc animated:YES];
[vc release];
And all works well when it comes to popping it off the stack with:
[self.navigationController popViewControllerAnimated:NO];
But now when I pop the vc off the stack I get a crash in main.m stating a bad access at line:int retVal = UIApplicationMain(argc, argv, nil, nil);
But now if I comment out [vc release] no more crash?
But why and surely this leaks memory as Im not releasing something I created?
Your memory management looks fine. Perhaps you are mismanaging the memory of something inside of your vc. What does the dealloc method of MyViewController look like?
My guess is you are using the incorrect init method (perhaps initWithNibName:bundle:) and you are releasing ivars in dealloc that were never properly initialized.
Did you try using it as a
#property
Navigation controller will retain vc then, when vc is popped, navigationController releases it and vc deallocs.
So, you must leave the release code, it is correct.
I think you have to use a initWithNibName:bundle: insted of the init.
The reason why they are different is that you are not allocating the text objects, and therefore you are not the owner. It is the IB's job to alloc and realese them, which it does.
So if you also try to release it, it will cause problems.
Perhaps you are mismanaging the memory of something inside of your vc.
This sentence from #brandontreb really helped me! I've struggled a whole day to fix the crash after a 'Received simulated memory warning', exactly descripted like:
Preventing bad access crash for popViewControllerAnimated in uinavigationcontroller setup
In my pushed view controller's loadView:, passed the view controller self to its dataSource's init:.
LayoutPickerDataSource *pickerDataSource = [[LayoutPickerDataSource alloc] initWithController:self];
while the dataSource class retained it like:
#property (nonatomic, retain) LayoutViewController *viewController;
Fix the crash just change to:
#property (nonatomic, assign) LayoutViewController *viewController;
and remove:
[viewController release];
bingo! I still don't know why! As viewController released in dealloc: of dataSource.
Related
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.
normally when I'm using a viewcontroller that will push the current viewcontroller out of the way, I use a UINavigationController and push/pop the new viewcontrollers and let them handle all the dealloc themselves.
However, for example, in this case, I have a MainViewController, which is the default view when the app starts up. I have a second view, called SecondaryViewController, that is a popup on the main screen (sort of like a lightbox).
Here is the code to illustrate:
//From within mainViewController:
secondaryViewController = [SecondaryViewController alloc] initWithNibName:#"SecondaryViewController" bundle:nil];
[self.view addSubview:secondaryViewController.view];
The secondaryViewController interface looks like this:
//interface
#interface SecondaryViewController : UIViewController
{
IBOutlet UILabel *httpLabel;
IBOutlet UIScrollView *scrollView;
}
#property(retain, nonatomic) IBOutlet UILabel *httpLabel;
#property(retain, nonatomic) IBOutlet UIScrollView *scrollView;
As for the implementation, I have the #synthesize for the #property ivars, but I'm not doing any manual allocs. However, I did put a dealloc method:
- (void)dealloc
{
[httpLabel release];
[scrollView release];
[super dealloc];
}
But I'm not sure I need the above.
So my questions would be the following:
1) Do I need the above dealloc method in this case? Or more generally, when would a subview need a dealloc method?
2) If I do or dont need it, does it depend on whether I'm adding the secondaryViewController via addSubview or pushViewController? For instance, if I wanted to replace the entire mainViewController, with this:
[self.navigationController pushViewController:secondaryViewController animated:NO]
Would the secondaryViewController need a dealloc method?
Thank you!
Yes, you do need the dealloc method exactly as you have it, in this case. You are on the right track because you're assuming that since you are not doing any manual allocating, you don't need to do any dealloc/releasing... however, by specifying the property as (retain, nonatomic), you are doing implicit retaining.
This means that if you ever set those properties, what's actually occurring under the covers is something like this:
-(void)setHttpLabel:(UILabel *)newlabel
{
if (newLabel != httpLabel)
{
[httpLabel release];
httpLabel = [newLabel retain];
}
}
As you can see, your synthesize is causing a retain to occur on an object. If you never balance that retain out with a release, it will leak. So the only logical place to put it, is in your dealloc method. This creates the circle of life.
If you never set these properties and don't have release in dealloc, then it won't leak anything, but you obviously wouldn't want to make those assumptions.
If you didn't have any retain properties or any manual allocing of ivars, then and only then, can you nuke the dealloc method.
Hope that helps.
I think this is allowed in the latest iOS 5+ but previously you were not supposed to add another viewcontrollers view to your main viewcontroller. This is clear misuse and can lead to issues.
The concept of viewcontroller is one who controls all the views. A view controller should not control another viewcontroller unless it is a container viewcontroller such as UINavigationController/UITabBarController.
So please rethink the design. why do you need the SecondaryViewController. Why cannot the mainviewcontroller manage the secondary view as well?
Lastly, every viewcontroller should have the dealloc in it.
If you need to access secondaryViewController from your main view controller after you've added its view to the hierarchy, you should not deallocate it at that point. If you don't need to access the secondary controller after you've displayed it, you can dealloc it at that point.
In practical terms, if secondaryViewController is an ivar, it probably makes sense to keep a retained reference to it. If it's a local variable and you're not accessing it later, you should dealloc it.
I have created a property of a viewController and retaining it from ClassB of viewController (Class A).
so basically I have #property (nonatomic, retain) ClassAViewControllerVC, and synthesized in the main file.
I have an IBAction in which I am allocating ClassAViewController and pushing it on navigation stack, but I am trying to analyze where should I release this viewController?
- (IBAction) response {
ClassAViewControllerVC = [ClassAViewController alloc] initWithNib:#"ClassAViewController" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:self.ClassAViewControllerVC animated:YES]
}
Is it okay to release the view controller after I stack it on the navigation-Controller as described above?
Also, is it a good idea to set property for such viewController at the first place? I started to notice that my apps started to crash if not utilized #property retain way. Any thoughts or concern would be appreciated.
Thanks
Firstly, no, it's not necessary to keep such an object in a property. You only need to keep objects in a property if the class will require access to the object later on. In this case, I think a local variable will do.
In this example, you create a ClassAViewController with alloc, meaning that the caller (this method) has responsibility to release it once it's finished with it.
When you add it to the navigation controller stack, the navigation controller retains it, because it keeps a reference to it.
So, at the end of this method, you should release it, but it's been retained by the navigation controller, so it's not deleted.
The code should look like this:
- (IBAction) response {
ClassAViewController *viewController = [ClassAViewController alloc] initWithNib:#"ClassAViewController"
bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:viewController animated:YES]
[viewController release];
}
P.S. it's convention in objective-C to write variable names starting with a lowercase letter. Uppercase starting letters are used for class names, and it confuses the bejeesus out of me! ;)
if ClassAViewControllerVC is a field of ClassB, you should release it in the dealloc method.
But you should probably create ClassAViewControllerVC in the init or even better viewDidLoad method.(note: if you create something in viewDidLoad, you need to release it viewDidUnload as well, not only in dealloc)
Because now every time "response" method is called ClassAViewControllerVC will be created all over again. (and there is not much sense storing it in the property). If this is what you want you probably should not make ClassAViewControllerVC a filed of your ClassB, but just have it as s local variable in response method like this:
- (IBAction) response {
ClassAViewController *classAViewController = [ClassAViewController alloc] initWithNib:#"ClassAViewController" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:classAViewController animated:YES];
[classAViewController release];
}
Best way to solve this problem is, drag and drop empty UIViewController in your ClassB.xib file. Create #property (nonatomic, retain) ClassAViewControllerVC, make the connection to empty UIViewController you just drop to classB.xib file and also change the NIB name to the class A nib file name. This will solve all the problem of crashing because you are not allocating or releasing any memory.
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;
I have a navigation controller and a table view. When someone click on the table view, I do the following:
MyViewController *myViewController = [[MyViewController alloc] initWithImage:image];
[image release];
[self.navigationController pushViewController:myViewController animated:YES];
[myViewController release];
myViewController will retain the image.
Now, if I go back and forth in the NavigationController, I get a leak because a new MyViewController gets created each time and apparently the popViewController doesn't release the myViewController.
My question:
Why doesn't popViewController release the controller?
How should I handle that? Put the myViewController as a member of my class and check if it already exists instead of creating it each time?
Thanks in advance for your help,
Apparently the problem was that this code was called in another thread and then this thread has to have another autorelease pool to let autorelease work correctly.
I usually declare the viewController once in the class, alloc in the init and push when needed. Then in the pushed view controller i adjust the view in viewWillAppear: