If I release, I get bad access, if I retain, I leak - iphone

I have a view controller that i'm trying to push onto the navigation stack. I create the controller with a local variable, set it up, then assign it to a property. Then if I release the variable, I get EXE_BAD_ACCESS. if I retain (or auto release) it leaks.
CustomTVC* controller = [[CustomTVC alloc]initWithStyle:UITableViewStyleGrouped];
controller.managedObjectContext = self.managedObjectContext;
self.tableViewControllerIvar = controller;
[self.navigationController pushViewController:self.tableViewControllerIvar animated:YES];
//[controller autorelease]; or [controller release]; or nothing
Here is exactly what happens if I release
The above code is fired from pushing an add button in the nav bar.
the view is pushed and everything is fine. In the new view I can push more views in over and over with no problem...unless
I go back to the root view of the navigation stack. (Which is where the above code is from).
Now if I drill down again to the second view, then try to push another it crashes.
Edit: I have a feeling that something is going wrong when I push the third controller onto the stack. With the push, it's inserting a new object into the managed object context which is causing the fetchedresultscontroller to update the tableview. There may be a broken pointer in there somewhere. I'll play with it and post the results. –
Edit: 5/16
Getting this error message in the log
* -[CustomTVC controllerWillChangeContent:]: message sent to deallocated instance 0x187270
This only happens after I pop the CustomTVC off the stack (go back to the navigation root view controller) I can push and save all I want as long as I don't pop the CustomTVC.

Fixed it. Had to set the fetched results controllers delegate to nil in viewDidLoad.
- (void)dealloc
{
self.fetchedResultsController.delegate = nil;
[_fetchedResultsController release];
[_managedObjectContext release];
[super dealloc];
}
seems the culprit was(according to the zombie instruments):
[NSFetchedResultsController(private methods) _managedObjectContextDidChange:]
Edit(s): Finally took the time to figure out how to put code in here correctly (I'm lazy)

autorelease should get the job done. When you assign tableViewControllerIvar, you can just call [controller autorelease]. This should take care of that.
CustomTVC* controller = [[CustomTVC alloc]initWithStyle:UITableViewStyleGrouped];
controller.managedObjectContext = self.managedObjectContext;
self.tableViewControllerIvar = [controller autorelease];
[self.navigationController pushViewController:self.VCTVC animated:YES];
If you are still getting EXE_BAD_ACCESS, then something else must be going on. Have you absolutely confirmed that this code, when run more than once, is causing the bad access?

you are assigning controller to tableViewControllerIvar
self.tableViewControllerIvar = controller;
and you are releasing controller, so you need to retain it in the above line
self.tableViewControllerIvar = [controller retain];
and when you are done with your "tableViewControllerIvar" then just release it, you shall not get any leaks after that

Related

NavigationController initWithRootViewController dealloc

I have some pretty simple code where I am using a UINavigationController and adding a rootViewController. After some processing has occurred I want to pop off the current view controller and replace it with another one. This seems to work fine, but my original view controller does not dealloc. I set a breakpoint in it's dealloc and it never gets hit. Below is my code. Not sure why happens. Just for testing if I release startController twice it does go away.
StartViewController *startController = [[StartViewController alloc] initWithNibName:#"StartViewController" bundle:[NSBundle mainBundle]];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:startController];
[nav pushViewController:startController animated:NO];
self.navController = nav;
[startController release];
[nav release];
Thanks any help.
Your ultimate goal is to bring the view controller's retain count to zero. So make sure that everything the view retains is released and anywhere the view is retained also release.
Please check the following possible causes:
Make sure you pop the view controller from the navController if you have a custom back button. The default Back button will work fine.
Make sure that all your IBOutlets are set to nil in viewDidUnload
- (void)viewDidUnload
{
[super viewDidUnload];
self.webView = nil;
}
If your view is an observer to a model class to receive events
For example
model.addObserver(myView);
and sure to also do
model.removeObserver(myView);
I hope this helps.
It looks as though your self.navController has got a holding reference to it. maybe put
self.navController =nil;
somewhere appropriate, so that when the view has been popped it is released.
I was trying to pop off the root view controller. I instead used the setViewControllers message from the UINavigationController object to replace all my view controllers.

Where is the leak?

The Instruments tells me that this piece of code has a leak. However, I am pretty sure I have released it later on. Can anyone tell me what's going on here?
- (void) addReminderEntry{
DataEntryController* item = [[DataEntryController alloc] initWithEntryType:REMINDER]; // it says that the leak was instantiated here
item.delegate = self;
[[self navigationController] pushViewController:item animated:YES];
[item setEditing:YES animated:YES];
[item release];// this is the place I release it
}
Thanks
More than likely it has to do with something not being released within the DataEntryController class. Make sure you are releasing all your properties/etc within that class.
Leaks tells you only where memory was allocated, what it cannot tell you is where to put the code that should have released it to start with!
So this is saying that you made a view controller, and it was still around in memory after you finished with it. Yes you release the VC in that code, but only after you present it - which means the navigation controller has retained it, and possibly other things. It only gets deallocated when the final release is called.
The main culprit for view controllers not being released is usually having the view controller set itself as a delegate for something it retains, and then not undoing that when the view controller goes offscreen. If your view controller is a delegate of something that retains it, it's never going to be deallocated.
It turns out that this is caused by this constructor:
- (DataEntryController*) initWithEntryType:(DataType) eType{
DataEntryController* item = [[DataEntryController alloc] init];//<- here
item.entryType = eType;
item.allowEdit = YES;
return item;
}
Apparently iOS adds retain 1 to each constructor with an initial 'init'.
It works fine after switching to:
DataEntryController* item = [super init];

when to alloc and initialize a view controller

recently I joined two Xcode projects together. To get this thing to work, I had to alloc and initialize my view controller.
self.myViewController = [[MyViewController alloc] init];
But why? In the other project I have the same code. The sole difference is hierarchy of the different views. I added a new view to the top (beginning). So the calling View Controller is not the first view on the stack anymore.
I'm pushing my view in this way on the stack:
[[self navigationController] pushViewController:myViewController animated:YES];
In my NIBs I have added a View Controller object with IB and connected the Outlets.
And I have a memory management question too: If I have a property like myViewController, do I have to release it? The "normal" release is done in the dealloc method. But do I have to use an additional release because of the alloc? I don't think so, but I ask you anyway.
I would need to see more code to answer why you had to alloc your view controller, but I'd say that you always alloc them manually (at least in my experience).
As for the memory management question, if your property is declared as a retain property (#property(retain) UIViewController *myViewController), you are indeed leaking memory, since the retain count after alloc will be 1, and after the retain done by your accessor will be 2. Hence, if you release it only once, you'll end up with a leak.
I usually do this instead:
self.myViewController = [[[MyViewController alloc] init] autorelease];
I found it out: In IB I had to set the nib name on my view controller object. So the alloc and initialization is done by IB?
There is one more option:
(IBAction)loginButton:(UIButton *)sender {
NSLog(#"pressed login");
ICMasterViewController *controller = [[self storyboard] instantiateViewControllerWithIdentifier:#"mainnav"];
[self presentViewController:controller animated:YES completion:nil];
}
On your storyboard you must have UIViewController with name mainnav

UINavigationController and memory management

- (void)launchSearch
{
EventsSearchViewController *searchController = [[EventsSearchViewController alloc] initWithNibName:#"EventsSearchView" bundle:nil];
[self.navigationController pushViewController:searchController animated:YES];
//[searchController release];
}
Notice the [searchController release] is commented out. I've understood that pushing searchController onto the navigation controller retains it, and I should release it from my code. I did just alloc/init it, after all, and if I don't free it, it'll leak.
With that line commented out, navigation works great. With it NOT commented out, I can navigate INTO this view okay, but coming back UP a level crashes with a *** -[CFArray release]: message sent to deallocated instance 0x443a9e0 error.
What's happening here? Is the NavigationController releasing it for me somehow when it goes out of view?
The boilerplate that comes on a UINavigationController template in XCode has the newly-pushed controller getting released. But when I do it, it fails.
---EDIT----
So this morning, I sit down, and it works. No real clue why. Sigh.
Taking what I thought I learned, then, and applying it to another piece of this same controller, I did the following. Yesterday I had this code WITHOUT the release statements, because it didn't work right with them. So this morning I added them to create:
- (IBAction)switchView:(id)sender
{
UISegmentedControl *seg = (UISegmentedControl *)sender;
NSInteger choice = [seg selectedSegmentIndex];
NSArray *array = [mainView subviews];
UIView *oldView = [array objectAtIndex:0];
[oldView removeFromSuperview];
if (choice == 0) {
tableController = [[EventsTableViewController alloc]
initWithNibName:#"EventsTableView" bundle:nil];
[mainView addSubview:tableController.view];
[tableController release];
}
if (choice == 1) {
calendarController = [[EventsCalendarViewController alloc]
initWithNibName:#"EventsCalendarView" bundle:nil];
[mainView addSubview:calendarController.view];
[calendarController release];
}
if (choice == 2) {
mapController = [[EventsMapViewController alloc]
initWithNibName:#"EventsMapView" bundle:nil];
[mainView addSubview:mapController.view];
[mapController release];
}
}
With it set up like this, when I come onto the view, the main portal of my view is filled with the EventsTableViewController's view, I can click to mapView and calendarView, but when I go BACK to tableView, I die because the table delegate methods are being called on a deallocated instance.
So I went and made all of these controllers into synthesized properties, so I can release them in [dealloc]. Which seems to work, but the real question is why adding these views as subviews doesn't retain them, passing ownership to the new view it's a member of, allowing me to release them right there?
Wow, guys. Thanks so much for all your responses--tragically I sent you all on a horrible goosechase.
My NavigationView navigates a NSArray of Event objects (local arts events). My table view drills down to a detail view.
My detail view has in it the following:
-(void)loadEvent:(Event *)event
{
thisEvent = event;
}
And I call that from my table view before pushing the detail view onto the nav stack. thisEvent is a synthesized property of type Event, and so since it's synthesized, I dutifully release'd it in [dealloc].
Many of you already see the problem. Backing up to the table view, when I scroll such that the one I just saw is displayed, it builds the custom table row, and so it goes to get the title property from the Event.... which I just released inside the detail controller. Boom.
I added a retain to that loadEvent: method above and the crashes, they are gone.
NONE of this was really about the views getting retained and released by the navcontroller. It was about accidentally over-releasing the data objects I'm navigating. Part of what had me discover this was, I NSLogged myself in the [dealloc] of each of these view controllers, and I can now see they're behaving exactly as they should.
Thanks! I SO love this site.
I'd guess the fault lies in EventsSearchViewController's init. Is it returning an autoreleased self by mistake ?
Looks like EventsSearchViewController is allocating an array and then over-releasing it, with one of the releases probably in its dealloc.
If you comment out the release, your EventsSearchViewController is never deallocated (it leaks). So, errors that occur as a result of its own dealloc will be masked since that method won't be called. Releasing the controller is the right thing, but you have another bug in the controller itself that only appears at dealloc time.
It could also be that dealloc is releasing an autoreleased array, so you may not have two explicit release calls in your code. But it looks very much like releasing something in dealloc that's causing the problem.

Why isn't removeFromSuperview hiding my UIView in my iPhone application?

I'm new to iPhone development and am having problems removing a sub-view from the main window. The problem is that the view still shows up even after calling removeFromSuperview.
The sub-view is created and added to the display tree through this code:
// Instantiate the controller for the authentication view
AuthenticationController* controller = [AuthenticationController alloc];
[controller initWithNibName:#"AuthenticationView" bundle:[NSBundle mainBundle]];
authController = controller;
// Add the authentication view to the window
[[stateManager appWindow] addSubview:[authController view]];
Then later, and I have verified that this code is run by setting a breakpoint, this is how I'm attempting to remove the view:
[[authController view] removeFromSuperview];
In case it matters, here's the dealloc code that does the for the owner of the view controller:
- (void)dealloc {
[authController release];
[super dealloc];
}
What is causing this sub-view to continue to show up?
I got this working. Apparently, a view doesn't go away until it's deallocated, and I had a misunderstanding of how memory management works on this platform. Here's my corrected code:
AuthenticationController* controller = [[AuthenticationController alloc]
initWithNibName:#"AuthenticationView" bundle:[NSBundle mainBundle]];
controller.delegate = self;
authController = controller;
[controller release]; // <-- Problem was that a reference was being maintained
[[stateManager appWindow] addSubview:[authController view]];
Not sure what you mean by "show up." On screen? In memory?
Your "fix" looks buggy, in that alloc gives you one reference, you then release it, which gets rid of the AuthenticationController. Which you then use.
This might appear to work since there hasn't been anyone overwriting the controller before you read its view, but this is just asking for trouble.