I am having memory leaks when I m using a view controller's view
My code sequence is like this
viewController1 = [[ViewController alloc] init];
destinationViewController = [[DestinationViewController alloc] init];
[destinationViewCOntroller useView:viewController1.view];
[viewController1 release];
[destinationViewController release];
And for testing purpose I have empty implementation in useView method. So my problem is viewController1 is never getting deallocated. I have made sure that no other place has any reference to viewController1.
When I remove the method call(useView) where I pass viewcontroller1.view then viewcontroller1 is deallocating properly.
Any ideas why the behaviour is like this?
This seems to be a rather academic problem. Just enable ARC and forget about it.
Related
I have a BaseViewController, which is a subclass of UITabBarController, and set up my views in this view controller.
-(void)setUpViews
{
FirstController *mainViewController = [[FirstController alloc] initAssignment:delegate.currentAssignment withMutableArray:myArray];
UINavigationController *firstNavController = [[UINavigationController alloc] initWithRootViewController:mainViewController];
SecondController *secondViewController = [[SecondController alloc] initWithNibName:#"SecondController" bundle:nil];
UINavigationController *secondNavController = [[UINavigationController alloc] initWithRootViewController:secondViewController];
self.viewControllers = [NSArray arrayWithObjects:firstNavController, secondNavController,nil];
firstNavController.tabBarItem.image = [UIImage imageNamed:#"blablabla.png"];
firstNavController.tabBarItem.title = #"Stream";
secondViewController.tabBarItem.image = [UIImage imageNamed:#"blabla.png"];
secondViewController.tabBarItem.title = #"Favourite";
}
Now I have another view controller, I call it ViewHandlerController, a subclass of BaseViewController. in my viewDidLoad in this viewHandler, i invoke setUpViews which is declared in BaseViewController. in the first screen of my app, when a Login button is pressed, I instantiate my ViewHandlerController, and presented my tabcontroller succesfully with nav controllers by using.
[[[UIApplication sharedApplication].windows objectAtIndex:0] addSubview:viewControllerHandler.view];
Inside my app. there is a logout button. I am using NSNotificationCenter to call my logoutMethod which is declared in my first screen. My question is, in this logoutMethod, how can I release the previously allocated objects to avoid memory pressure since the user can log in again (logIn - logOut -logIn)? since I'm using ARC, is setting my ViewController to NIL will do all the clean up?
EDIT: is removing my ViewControllerHandler from superview and setting it to nil helps releasing its subviews too?
Cheers
Well, answer for your question (not ARC) – no, basicaly view controller doesn't releases his properties when release. But you should nil your properties in viewDidUnload and (or) dealloc methods.
If you use ARC, you should notice that some actions can retain your controller, and it can never be deleted in some cases. Watch for methods, which takes object for delegate, they may not using weak references
Have a look at this Apple article about memory management.
You can just use autorelease in alloc methods or for (UIView *view in [self.view subviews]) {view release}; in dealloc.
In fact release is opposite operation to retain. When you do retain, you increase by 1 count of object instances in allocated memory. It happens when you call alloc, copy, new, mutableCopy. If you are using ARC you can't release objects, memory management is not your problem already.
I have to show one popOver inside the left side of one splitController, I initialize the popOver whit an navigationController. But when i show the popOver my app crash.
Impostazioni *settings = [[Impostazioni alloc] initWithStyle:UITableViewStyleGrouped];
settings.title = NSLocalizedString(#"SETTINGS", nil);
settings.contentSizeForViewInPopover = kContentSizeOfPopOver;
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:settings];
nav.navigationBar.tintColor = kTintColorNavigationBar;
nav.contentSizeForViewInPopover = kContentSizeOfPopOver;
UIPopoverController *popOver = [[UIPopoverController alloc] initWithContentViewController:nav];
[popOver presentPopoverFromBarButtonItem:self.navigationItem.rightBarButtonItem permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES];
This is my code. Any ideas?
EDIT: Crash even if I set only a viewController instead of SplitController :/ And with a empty ViewController :/
(Possibly duplicate of Error using UIPopoverController.)
In short, you need to retain the UIPopoverController somehow. Either by defining a property for it or by managing the ref count manually. With ARC, the latter is not an option, so you need to store the reference.
I believe you need an instance variable to hold the popoverController. Otherwise after the method that contains the code that you showed finishes nothing will have retained your popover. Unlike when you add a subview to a view which the view would then retain the subview. The same thing does not take place for popovers.
Any help is appreciated ! It's several days I'm fighting w/o results.
The scenario:
I and iPad application have a SplitViewController that shows 2 controllersViews (Root on the left e Detail on the right)
The Root allows a recursive navigation (tree that could be several drilldown levels) and I'm calling every time the same controller class (UITableView) pushing always in the controller stack). When the user taps a cell (left side), the detail view (right side) shows the information.
Keep in mind that the detail view controller is not always the same class: it means that I'm allocating (and releasing) programmatically several detailView controllers according the kind of information I have to display.
Here the fragment:
UIViewController <ItemGenericViewController> *newDetailViewController = [[NSClassFromString(cntrClass) alloc] initWithNibName:cntrXib bundle:nil];
//the detailViewController has been defined in the head section as ItemGenericViewController
//each detailViewController is a subclass of ItemGenericViewController
detailViewController = newDetailViewController;
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:detailViewController];
// Update the split view controller's view controllers array.
NSArray *viewControllers = [[NSArray alloc] initWithObjects:self.navigationController, nav, nil];
self.splitViewController.viewControllers = viewControllers;
[nav release];
[viewControllers release];
[detailViewController release];
Everything is working fine until a memory warning arises.
From that moment if I try to display a new detailViewcontroller the "connection" in the SplitViewController, between the RootController and the detailController, seems vanished. The result is: nothing appear on the right part of the splitController.
In the mean time if I navigate to parent level in the root controller the situation still failing.
For your information each time I push in the stack a new RootController instance (left column) I'm releasing the same controller (to save memory as usual) and I suspect, after receiving the memory warning, iOS is trying to free itself memory and my "history" disappear and the related connection, throught the split controller, too.
Is a nightmare ;-)
Do you have any suggestion ?
Thanks
Dario
I had a similar problem to you (maybe even worse - 16 combinations of possible view switches)... But I believe i have solved it right now.
So, i believe you have used Apple's example for view switching (I have, with modifications), and if you have so, problem is that "root" splitViewController (from MainWindow.xib) get's "niled" as default behavior when memory warning. And even if you add new array of view controllers to it, it will not cause any change (and even worse, it will not show any sign of warning). And solution is to check is it nil, and if is, to reinitialize it.
here is the code, using example from above:
UIViewController <ItemGenericViewController> *newDetailViewController = [[NSClassFromString(cntrClass) alloc] initWithNibName:cntrXib bundle:nil];
//the detailViewController has been defined in the head section as ItemGenericViewController
//each detailViewController is a subclass of ItemGenericViewController
detailViewController = newDetailViewController;
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:detailViewController];
// Update the split view controller's view controllers array.
NSArray *viewControllers = [[NSArray alloc] initWithObjects:self.navigationController, nav, nil];
/**** Milos Edit ****/
if (self.splitViewController == nil) {
// I'm keeping reference in app delegate, but any way to reinitialize splitViewController is OK
self.splitViewController = delegate.splitViewController;
}
/**** end of edit ****/
self.splitViewController.viewControllers = viewControllers;
[nav release];
[viewControllers release];
[detailViewController release];
Hope it will be helpful.
Cheers
Milos
I was under the impression that adding a subview to a view goes like this:
UITableViewController *sitesel = [[UITableViewController alloc] initWithStyle:UITableViewStyleGrouped];
sitesel.view.frame = CGRectMake(0,0,100,100);
[self.left addSubview:sitesel.view];
[sitesel release];
But it seems I should not release the sitesel (the controller)?
So should I release the view or what, I had this retain stuff nailed a while ago, but it's slipped. (And to use a TableView, you have to subclass UITableViewController right?)
(self.left is a subview of self.view, added in a nib)
addSubview does retain the view, that's not the problem. Your issue is that the controller for the view goes away a little later.
You shouldn't release the view, because that's none of your business. You didn't create it, you didn't touch it. Leave it alone.
In order to keep things working, it needs to stay connected to a valid controller. Hence, you must not release the controller, but keep it around. Add a property like #property(retain) UITableViewController *siteController; and then do self.siteController = sitesel; before you release the controller. This way everything stays in memory.
PS: For cleanness, you should probably change the view in the accessor for sitesel. Just to make sure it always comes and goes along the controller. Your method would then get even shorter, just setting the controller.
ADDED: That setter could look like that, requiring you to set only the controller and the view being updated transparently:
- (void)setSiteselController:(UITableViewController *)ctrl {
if (_sitesel)
[_sitesel.view removeFromSuperview];
[_sitesel autorelease];
_sitesel = [ctrl retain];
if (_sitesel) {
_sitesel.view.frame = CGRectMake(0,0,100,100);
[self.left addSubview: _sitesel.view];
}
}
Your original code will then shrink to this much cleaner version:
UITableViewController *sitesel = [[UITableViewController alloc] initWithStyle: UITableViewStyleGrouped];
self.siteselController = sitesel;
[sitesel release];
PPS: You don need an controller for a UITableView to work. It's just much simpler!
I'm developing an iPhone app, I'm trying to push a view into the navigation controller, which I've done many times before, however, I'm having some issues with this particular app. I have a table view, and when the user selects one row the new view is pushed into the controller:
DataWrapper *row=[[self.rows objectAtIndex:[indexPath section]] objectAtIndex:[indexPath row]];
DataViewController *nextController=[[DataViewController alloc] initWithNibName:#"Data" bundle:[NSBundle mainBundle]];
[nextController setInfo:row];
[nextController setRow:[indexPath row]];
[nextController setParent:self];
[self.navigationController pushViewController:nextController animated:YES];
[nextController release];
and it goes fine, until the user taps the back button, I get an exception, and used NSZombieEnabled and get this:
-[DataViewController respondsToSelector:]: message sent to deallocated instance 0x4637a00
So i tried to remove the [nextController release] and in fact it worked, but WHY???? I allocated nextController, so I'm supposed to release it, right?? I don't feel right releasing this app if there's something like this, I feel like it's going to fail. Please let me know your thoughts.
Your nextController isn't being retained by navigation controller. If you release it then because there is only one init/release pair, the object is deallocated. Later when the navigationController attempts to send messages to it, you get the error you see.
This is also why remove [nextController release] fixes the problem.
You are right in that if you allocated, you should free it. But the caveat is only after your application is done with it, not before.
Some objects will stay allocated for nearly the lifetime of the application, so don't feel too bad.
I would guess that [self.navigationController] is returning nil, because if it weren't nil, it would be retaining your object. Since your object is not getting retained, it would appear that there is no object that's trying to retain it, indicating that the navigationController property is empty.
Is it possible that your navigation controller is somehow being deallocated, resulting in the view controllers also getting released? You could maybe test it by retaining the nav controller just before pushing nextController.
For debugging purposes, I would override -dealloc in your DataViewController class, and set a breakpoint on it.
but it seems to work for something like:
DetailViewController *controller = [[DetailViewController alloc] initWithNibName:#"SomeView" bundle:nil];
controller.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController: controller animated:NO];
[controller release];
so, why is it not working for pushViewController ?