MapKit/Location Manager crashes app when unloading view - crash

I has a bug where my application crashed "EXC_BAD_ACCESS" when I hit the back key on my navigation bar and the view unloaded that had a MapKit (mapView) and used the Location Manager. Tried for days to fix the bug and finally came up with a fix for anyone that comes across this problem:
Add this code to your dealloc
- (void)dealloc {
mapView.delegate = nil;
locationManager.delegate = nil;
[mapView release];
[locationManager release];
}

I had this one too, :) And, yes, this fix is actually a proper fix;
- (void)dealloc {
mapView.delegate = nil;
locationManager.delegate = nil;
[mapView release];
[locationManager release];
}
What happens behind the scenes is this:
You hit the backkey. This unloads and in consequence releases the controller which holds the mapView. As there has been quite likely only a single reference to the controller it will be dealloc'ed then.
The locationManager, however, is quite likely still referenced somewhere in the inner workings of geopositioning.
If the locationManager and/or mapView now send out a notification to their respective delegate, they are following an invalid pointer. Which will result in a EXC_BAD_ACCESS exception.
Yes: nilling delegates that point to self is always a good idea. I justed wished Apple would add some automagic there.

Related

UIViewController not being released when popped

I have a table view that when a cell is selected it pushes a view controller onto the navigation stack:
SAPostTableViewController *postViewController = [[SAPostTableViewController alloc] initWithNibName:NSStringFromClass([SAPostTableViewController class]) bundle:nil];
postViewController.site = site;
[self.navigationController pushViewController:postViewController animated:YES];
[postViewController release];
SAPostTableViewController has a static tableView which, and it's cells, are loaded from a nib.
I have overridden the initWithNibName:bundle: method:
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
self.sections = [NSMutableDictionary dictionary];
}
return self;
}
sections is a retained property.
In viewDidLoad of SAPostTableViewController I have this:
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(cellVisibiltyChanged:) name:#"SAStaticCellVisibiltyChanged" object:nil];
}
and so to match in viewDidUnload:
- (void)viewDidUnload
{
[super viewDidUnload];
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"SAStaticCellVisibiltyChanged" object:nil];
}
However when I press the back button in the navigation bar (all standard behaviour, no override) and SAPostTableViewController is popped, it doesn't call viewDidUnload or dealloc. So this means that if I then reselect the cell that pushes SAPostTableViewController it creates a new instance of SAPostTableViewController and repeating this back and forward just means the memory usage keeps increasing as the popped SAPostTableViewControllers never get deallocated. (I know this by running Instruments on allocations)
The weird thing is that if I release SAPostTableViewController twice then it works as I'd expect:
SAPostTableViewController *postViewController = [[SAPostTableViewController alloc] initWithNibName:NSStringFromClass([SAPostTableViewController class]) bundle:nil];
postViewController.site = site;
[self.navigationController pushViewController:postViewController animated:YES];
[postViewController release];
[postViewController release];
(If I add a third release statement, it crashes as I'd expect it to with just 2)
I have resorted to using retainCount and stepped through the lines of code the are executed in the first line of the directly above code, the retainCount remains at 1. It jumps up between the first and second line, so I can't see anywhere it is being retain an extra time?
The SAPostTableViewController is only used in this place, it is not a delegate of anything, nor does it have a delegate.
How can I find a fix, or is it something simple I've missed?
Here is what Instruments shows after pushing SAPostTableViewController just once (with only one release statement):
And what it shows after navigating back and forth repeatedly (again, one release statement):
You are creating a new Object when you clic one Cell, why you don't create your Object (SAPostTableViewController) in the init Method and then Push the same object watch time you click in Cell
you can do something like this :
postViewController = [[SAPostTableViewController alloc] initWithNibName:NSStringFromClass([SAPostTableViewController class]) bundle:nil];
and in the
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
postViewController.site = site;
[self.navigationController pushViewController:postViewController animated:YES]; [postViewController release];
}
I don't know what your problem is, but here are some things to consider:
You should absolutely NOT release the view controller twice. If you have indeed discovered a memory leak in UIKit (which is unlikely), then it is likely it would be fixed in a future version of UIKit. That means that anyone running an old version of your app on a new version of the operating system would experience nothing but crashes (due to over-releasing the view controller). It is better to leak than to crash. A leaky app is still a usable app (at least until you leak too much). But a crashing app can't be run at all.
-viewDidUnload is not doing what you think it should be doing. It is only called when the view controller's view is unloaded due to memory pressure. It is not called during normal deallocation. It would be wiser to rely on -viewWillAppear: and -viewDidDisappear: instead.

Not calling [super dealloc] inside a static UIViewController subclass

In my app I have a UIViewController subclass (VC for short) that I only use in one place in the entire app. In that place, I have been creating and pushing it like this:
MyViewController* VC = [MyViewController new];
[self.navigationController pushViewController:VC animated:YES];
[VC release];
but I was thinking that since this is the only place I am using a view controller of this type, I could do something like this so the settings used won't be reset each time the view controller is pushed onto the stack:
static MapsToSendPicker* VC = nil;
if(!VC) {
VC = [MapsToSendPicker new];
}
[self.navigationController pushViewController:VC animated:YES];
[VC release];
The problem with that code is that in VC's dealloc method, I release all of my instance variables and set them to nil, and finally I call [super dealloc]. This deallocates the static view controller, but the test if(!VC) isn't evaluated to true after (this would defeat the purpose of the whole idea if it were; then I'd have to recreate the view controller each time anyway).
My solution is overriding the dealloc method in MyViewController and not calling [super dealloc] at the end. This works, but the compiler raises a warning. How can I get rid of that warning while maintaining the functionality I gain with this design? Thanks!
Edit:
After a quick Google search, I have found this solution:
- (void)dealloc {
if(NO) {
[super dealloc];
}
}
but I would like something a little bit... cleaner. Any thoughts?
Remove the [VC release]; line and add [super dealloc] back. Everything will work properly and dealloc will never get called. Generally you should consider using NSUserDefaults in order to restore the VC properties instead of keeping the controller in memory all the time.

MobFox iOS Framework, Crashes When XIB unloaded... what am I doing wrong here?

A quick rundown of whats happening here.
I've got a GameScene and a GameOverUIController. When Game ends, It loads the GameOverUIController, which loads the xib, which loads the MobFoxBannerView.
Here's the issue: If you push the "play again" button to fast or you play a round longer than about 3 minutes, the ad loads in the background but essentially has no where to go, and crashes.
I took a friends advice of adding the ViewDidUnload to set the bannerview to nil, but it still crashes... I'm not the most amazing programmer admitted, but I feel like I'm making the dumbest error somewhere... Any help is greatly appreciated. Am I just using the wrong syntax on something here?
I've done the following things.
I synthesize bannerView on the GameOverUI Implementation.
Manually adding it to the view now, before I had added a UIwebview directly to xib Interface builder for GameOverUIController
self.bannerView = [[[MobFoxBannerView alloc] initWithFrame:CGRectMake(0, 0, 320, 50)] autorelease];
bannerView.delegate = self;
[self.view addSubview:bannerView];
[self.view bringSubviewToFront:bannerView];
And thats in my main implementation. So everytime it loads the GameOver Screen it requests a new add.
Then I set the values in the header file.
-(NSString *) publisherIdForMobFoxBannerView:(MobFoxBannerView *)banner;
#property (nonatomic, retain) MobFoxBannerView *bannerView;
and set my publisher ID in the the bottom of GameOverUIController.m
- (NSString *)publisherIdForMobFoxBannerView:(MobFoxBannerView *)banner
{
return #"xxxxxxxxxxxx";
}
Then... STILL get a crash so then I tried to set it to nil hoping that would work... but I'm not sure about the syntax...
On GameOverUIController.m
- (void)viewDidUnload
{
[super viewDidUnload];
self.bannerView = nil;
// or ? bannerView.delegate = nil;
}
Niether of those lines works..... I'm at a total loss. Going to move onto another ads platform unless I can figure this out before the weekend is over. Bane of my existence right now. Any help would be greatly appreciated. Thank you very much
Edit:
Figured it out...
Had nothing to do with ViewUnload....
- (void)dealloc
{
bannerView.delegate = nil;
[super dealloc];
}
Was adding it to the wrong location... Works perfectly now.
I guess your problem is in
bannerView.delegate = self;
when add is loaded (because of bad internet it can be very slow for instance) - delegate method is being called, but "self" is released - so runtime exception is thrown.
By the way, I don't know a thing about MobFox framework. It's just a common anti-pattern.

Dealloc Being Called Twice?

Resloved!
Thanks to Lone Gunman, this issue was due to an oversight of not setting the many delegates to nil before releasing them.
This is a strange one... I'm familiar with basic memory management but I think something is unusual about what I am seeing. Here is a little background...
I have a NavigationController that handles the navigation between the following ViewControllers:
Home -> Games -> Game
When running the code it falls down when leaving the Game. Within the GameViewController there is a dealloc method that resembles:
- (void)dealloc
{
[board release];
[opponentsViewController release];
[instructionsViewController release];
[imgPicker release];
[gameView release];
[super dealloc];
}
When the navigation controller goes back to the Games list (from the Game) it throws a EXC_BAD_ACCESS. So I bring up my trusty profiler and check for Zombies. Alas, just as I expected a message is being sent to a deallocated object! Digging deeper I find there to be 3 entries in the object's history:
Board getting alloc'd (called by Game's init method)
Board getting released (called by Game's dealloc method)
Board being Zombie'd (called by Game's dealloc method)
Both calls 2 and 3 are called from UINavigationController setDisappearingViewController.
In my dealloc method I set breakpoints to each release call, when doing so - the [board release] call occurs, then the [opponentsViewController release] call occurs then the [board release] call occurs again. So I'm seeing the dealloc method does not finish completely and calls again.
What might be causing this?
Edit: This is the GameViewController Implementation
Code from the Games controller that adds this game:
-(void) gotoGame:(int)tag {
game = [[GameViewController alloc] init];
[self.navigationController pushViewController:game animated:YES];
[game release];
}
Edit: This is the GameViewController Header
I would try setting all your ivar's delegates to nil (EDIT: in dealloc). I've had a similar problem with a fetched results controller. Failed to set the its delegate to nil in dealloc and the core data stack still had a pointer to it when the view controller was released.
So that's my bet, set ivar delegates to nil in dealloc, although I can't see your header to know what protocols your are conforming to be sure.
EDIT: Explanation
Setting a delegate is actually giving the object that is doing the delegation a pointer (I believe it usually an assigned property).
#property (assign) delegate;
I'll use the problem I had as an example.
So let's say you have a view controller that has a fetchedResultsController as an ivar. When you set the FRCs delegate:
fetchedResultsController.delegate = self;
and the view controller gets released, any object that is using that pointer still thinks it's live. You would think since the FRC is getting released in dealloc as well, you'd be fine(which is why it took me 4 days to figure this out :) ), but sometimes other parts of an implementation use your delegate as well. So the fix is:
-(void)dealloc
{
self.fetchedResultsController.delegate = nil;
[_fetchedResultsController release];
[super dealloc];
}
Note: as soon as the new tools are available to everyone you won't have to worry about this stuff anymore ^ ^;
try
- (void) dealloc {
if(game != nil){
//release here
[game release];
}
[super dealloc];
}
By the way it seems you have declare game in header file and just after pushing you are releasing it and also in dealloc method you are releasing it. Either remove the release call from dealloc method or change you method like this.
-(void) gotoGame:(int)tag {
GameViewController *game = [[GameViewController alloc] init];
[self.navigationController pushViewController:game animated:YES];
[game release];
}
UPDATE
Also you are not using the tag anywhere. Why don't you create your init method like this
GameViewController *game = [[GameViewController alloc] initWithTag:tag];
[self.navigationController pushViewController:game animated:YES];
[game release];

Why release a property that you've already set to nil?

Here are two methods in a view controller from an Apple tutorial:
- (void)viewDidUnload {
self.eventsArray = nil;
self.locationManager = nil;
self.addButton = nil;
}
- (void)dealloc {
[managedObjectContext release];
[eventsArray release];
[locationManager release];
[addButton release];
[super dealloc];
}
Couldn't the dealloc method be shortened to the following? If not, why not?
- (void)dealloc {
[managedObjectContext release];
[super dealloc];
}
- (void)viewDidUnload is not guaranteed to be called, so you should always release things in dealloc too.
See this question to find out when it's called, and what you should do when it is.
No, because you cannot rely on viewDidUnload being called upon deallocation. viewDidUnload is only called when the view controller receives a memory warning while its view is not on screen. If the view controller gets deallocated, viewDidUnload is not called (AFAIK, I'm not entirely sure).
because it's a good practice to always cleanup your ivars in dealloc. something could go wrong, or you may encounter an execution you do not expect.
Setting eventsArray to nil just means it has no content, but still space for content
Calling [eventsArray release] releases the space the array consumed.