Need help fixing iPhone memory leaks! - iphone

I encountered some strange memory leaks executing following code on iPhone device:
#implementation TestViewController
#synthesize myButton;
- (IBAction)buttonPressed {
ABPeoplePickerNavigationController* selectContactViewController = nil;
selectContactViewController = [[ABPeoplePickerNavigationController alloc] init];
selectContactViewController.peoplePickerDelegate = self;
[self presentModalViewController:selectContactViewController animated:YES];
[selectContactViewController release];
}
Releasing the picker simple done as follows:
- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker {
[self dismissModalViewControllerAnimated:YES];
}
Instruments marks "selectContactViewController = [[ABPeoplePickerNavigationController alloc] init];" as leaking. Any idea why?

You might want to construct your Picker control like so:
ABPeoplePickerNavigationController* selectContactViewController = nil;
selectContactViewController = [[[ABPeoplePickerNavigationController alloc] init] autorelease];
selectContactViewController.peoplePickerDelegate = self;
[self presentModalViewController:selectContactViewController animated:YES];
When you present the modal view controller, it will retain the view on its own. That's how it's able to still pass you an instance of the view controller to your delegate. Best bet is to set the view controller to be autoreleased, so when it gets popped from the navigation controller, the NSAutoReleasePool will garbage collect it.

Just a comment - do you use any protocol like UINavigationControllerDelegate in the interface declaration?
I encountered a situation where just referencing this protocol caused a similar leak message.

Related

unable to switch from multiple views

I have a simple app with only location services and 3 (almost empty) different views, and from some reason I can't get from view 1 to view 2 - app crashes and I get an exception. View 1 is the original .xib file, the two others are just views that I added later. It's weird cause I can switch between all of them (1->3, 2->1, 2->3, etc..) just not from 1->2.
I use this code in #1 view controller m. file:
- (IBAction) switchToMaps : (id)sender //this is the one that doesnt work
{
MyMap *mapsView = [[MyMap alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:mapsView animated:YES];
}
- (IBAction) switchToThird : (id)sender
{
ThirdView *third = [[ThirdView alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:third animated:YES];
}
and as another example, here is the code from the 2nd view controller (MyMaps.m):
- (IBAction) switchBack : (id)sender
{
LastLocationViewController *firstView = [[LastLocationViewController alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:firstView animated:YES];
}
- (IBAction) switchFront : (id)sender
{
ThirdView *lastView = [[ThirdView alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:lastView animated:YES];
}
I know it's super vague, but any ideas what can cause this? I have no idea how to debug this...I even put breakpoints at the beginning of each IBAction method, and when it crashes, it doesnt even stop there....before I added this code, this app (which has only location) worked totally fine.
Any ideas?? Thanks!!
if your view does not load from any nib file then you should do like
MyMap *mapsView = [[MyMap alloc] init];
and
ThirdView *lastView = [[ThirdView alloc] init];
and in your back method
- (IBAction) switchBack : (id)sender
{
// LastLocationViewController *firstView = [[LastLocationViewController alloc] initWithNibName:nil bundle:nil]; // because you are allocating new memory to your last view
// [self presentModalViewController:firstView animated:YES];
[self dismissModalViewControllerAnimated:YES];
}
- (IBAction) switchFront : (id)sender
{
// ThirdView *lastView = [[ThirdView alloc] initWithNibName:nil bundle:nil];
// [self presentModalViewController:lastView animated:YES];
[self dismissModalViewControllerAnimated:YES];
}
My hunch is that you're throwing an exception because
MyMap *mapsView = [[MyMap alloc] initWithNibName:nil bundle:nil];
is failing to load a nib. Without seeing your console output it's impossible to say for sure. So a few things to try:
Comment out [self presentModalViewController:mapsView animated:YES];, see if it still crashes.
Explicitly name the nib you expect to load. The nib loader assumes the nib is named exactly the same as the view controller if you pass in nil. So if you don't have a match, you'll end up with an exception (Like this [[MyMap alloc] initWithNibName:#"NibNameWithoutExtension" bundle:nil];)
Set a breakpoint at [self present... and then hover your mouse over "mapsView" after execution pauses. If the popup thing shows you mapsView is nil, you know your trouble is trying to pass a nil object to -presentModalViewController:animated:. If your breakpoint never hits because you throw an exception first, well, there you go, the trouble is a line above.
edit:
One more thing. If your nib has a button that's wired to an action that no longer exists, that would definitely get you in trouble. Inspect each button and make sure no actions are labeled in yellow, indicating a mismatch between the button's target and the actions it's reporting to IB. This would definitely account for the breakpoint behavior you described.

Bad memory leak when creating a view - even though a 'release' is present?

Part of my code presents a UITableViewController in the following way:
FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:#"Settings" bundle:nil];
flipside = [[UINavigationController alloc] initWithRootViewController:controller];
controller.delegate = self;
flipside.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:flipside animated:YES];
[flipside release];
[controller release];
Using the leaks tools, no memory leaks are picked up on. However, whenever I bring up the settings menu (as shown in the code above), more memory appears to be allocated and never released - almost 100 kB every time.
Weirdly, the inclusion of the two release statements at the end seems to have no effect on the memory allocation..? Is there something I am misunderstanding about memory allocation in objective-c, or is something weird going on?
Any ideas are much appreciated - thanks!
If flipside is a retained property then the navigation controller is leaking. the problem is that you are bypassing the accessor method and releasing flipside directly. This is just messy code. A better way to do it would be to make an accessor method for flipside that will only alloc a new one if you haven't already created one. It's called lazy loading. To do this, just leave the #synthesize for flipside (but you shouldn't set it from outside the accessor method), in your header file change the property to, and add this method to the implementation:
- (UINavigationController *)flipside {
if (flipside != nil) {
return flipside;
}
FlipsideViewController *controller = [[[[FlipsideViewController alloc] initWithNibName:#"Settings" bundle:nil];
controller.delegate = self;
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:controller];
[controller release];
navController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
// This implies a retain if your flipside property is set to retain
self.flipside = navController;
[navController release];
}
make sure to put self.flipside = nil in the viewDidUnload method of the view controller the code you included came from (I assume it's a presentSettings action).
what your presentSetting action should now look like is this:
- (IBAction)presentSettings {
// make sure you use the accessor self.flipside instead on accessing the variable directly
[self presentModalViewController:self.flipside animated:YES];
}

Properly release ViewController when adding subview without navigationController

Something I run into a lot is not being able to create and destroy a ViewController properly when adding the ViewController.view as a subview not on a navigation controller.
for example:
MyViewController *myViewController = [[MyViewController alloc] init];
[currentView addSubView:myViewController.view];
[myViewController release];
That Works great if it is a controllerless view and there are no UIControls that the user must interact with. But sending messages to the view controller of that view causes EXEC_BAD_ACCESS because they are no longer in memory.
MyViewController *myViewController = [[MyViewController alloc] init];
[currentView addSubView:myViewController.view];
This works when sending messages, however it is a memory leak and is caught by the static analyzer.
Setting it as a property of the current view controller works sometimes. But if I need to create a bunch with an unknown number of MyViewControllers and add them to something like a UIScrollView, that doesn't work either.
for (int i = 0; i < [myViewControllers count]; i++) {
MyViewController *myTmpViewController = [[MyViewController alloc] init];
[myCurrentUIScrollView addSubview:myTmpViewController.view];
[myTmpViewController release];
}
Still gonna crash if myTmpViewController has user interaction or something similar. How does one go about adding this, and releasing it properly?
You can have a NSMutableArray and add the controllers there.
for (int i = 0; i < [myViewControllers count]; i++) {
MyViewController *myTmpViewController = [[MyViewController alloc] init];
[myCurrentUIScrollView addSubview:myTmpViewController.view];
[myControllers addObject:myTmpViewController];
[myTmpViewController release];
}
// ..
- (void) dealloc {
[super dealloc];
[myControllers release];
}
You could store a pointer to the view controller in an ivar and then release it in your dealloc method.
If such a subview has limited 'controlling needs', then you might consider to subclass from UIView and have the view control itself (e.g. Be its own delegate)
Otherwise you need to decide for the most logical 'owner' of these viewcontrollers (often the viewcontroller of the parentview) and make them ivars of their owner.

UIViewController either leaks memory or crashes app if autoreleased

I have this code in my app and it says memory leak at 'gvc'.
GameViewController* gvc = [[GameViewController alloc] init];
[self.navigationController pushViewController:gvc animated:YES];
If i modify this code to autorelease view controller, it crashes my app after a while giving error 'Missed Method'
GameViewController* gvc = [[[GameViewController alloc] init] autorelease];
[self.navigationController pushViewController:gvc animated:YES];
Is there something wrong with autorelease? How to resolve this memory leak?
Thanks in advance.
You can safely do this:
GameViewController* gvc = [[[GameViewController alloc] init] autorelease];
[self.navigationController pushViewController:gvc animated:YES];
or this:
GameViewController* gvc = [[GameViewController alloc] init];
[self.navigationController pushViewController:gvc animated:YES];
[gvc release];
They are functionally equivalent and don't cause memory leaks or crashes. Look at your code after [self.navigationController pushViewController:gvc animated:YES]; to see if you over-release gvc.
Just release it when you're all done. If you need it all the time, then put GameViewController *gvc; in the header and put [gvc release]; in the dealloc method.
The obvious solution is simply to release the view controller once you've pushed it onto the nav controller.
i.e.:
GameViewController* gvc = [[GameViewController alloc] init];
[self.navigationController pushViewController:gvc animated:YES];
[gvc release];
That said, I'd be surprised if the autorelease was actually causing a memory leak at all.
It may be everything. I mean, your first code is obviously leaking, and the second code snippet is ok at first glance, but we don't know what you implement inside that class. Imagine in GameViewController, you have a string property userName, and in its dealloc, you do something like:
- (void)dealloc
{
[userName release];
[userName release];
[super dealloc];
}
Then of course it crashes when you release the controller. You should use gdb backtrace (type "bt" when it crashes) to see exactly where it crashes, or better yet, try to enable NSZombie and use instruments to find out.
Btw as a rule of thumbs in iPhone/iPad programming (where resources are scarce), try to avoid autorelease whenever you can.

Navigation to another view in iphone

here is my code , i am trying to get from one view to another without any memory leaks.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
firstviewcontroller *first = [[firstviewcontroller alloc] init];
[window addSubview:first.view];
[self.window makeKeyAndVisible];
return YES;
}
-(IBAction)gotosecondview:(id)sender
{
secondviewcontroller *second = [[secondviewcontroller alloc] init];
[self.view addSubview:second.view];
[second release];
}
-(IBAction)gotofirstview:(id)sender
{
[self.view removeFromSuperview];
}
to make the above code work without crashing , all i have to do is remove [second release].
if I remove it I get memory errors (build and analyze) . how can i solve this problem. and i dont want to use [self.navigationController pushViewController:second animated:YES];
all i am trying to do i navigating from one view to another and vice versa WITHOUT using navigation controller. my firstviewcontroller and secondviewcontroller are of type UIViewController.
Thanks in advance.
You need to keep the current view controller alive while its view is showing (so it can process the user input, etc.).
In your code, you can achieve that in several ways:
Keep an instance of firstviewcontroller and secondviewcontroller as instance variables, and release them on the dealloc method.
Keep an instance variable with the currently in use UIViewController and release it when you switch to another view.
The code for the second option would look something like this:
#interface
UIViewController *currentViewController;
#end
#implementation
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions {
firstviewcontroller *first = [[[firstviewcontroller alloc] init] autorelease];
[self switchToViewController:first];
[self.window makeKeyAndVisible];
return YES;
}
- (void)switchToViewController:(UIViewController *)aViewController {
[currentViewController.view removeFromSuperview];
[currentViewController release];
currentViewController = [aViewController retain];
[self.window addSubview:currentViewController.view];
}
-(IBAction)gotosecondview:(id)sender {
[self switchToViewController:[[[secondviewcontroller alloc] init] autorelease]];
}
#end
Here, all the logic for maintaining a single UIViewController alive lies in the switchToViewController method, which also handles the logic for switching from one view to another. As an added bonus, you can quickly add support for animations by adding a couple of lines in switchToViewController.
You can not release view in the call.
There is only one thing you can do in such conditions. use Autorelease,
The reason [second release] is crashing your code is likely because you're releasing your view controller which in turn releases the second view. The iPhone cookbook has some sample code on switching/swapping views if that's all that you're trying to accomplish. Here's the link. Hope this helps!
link text