0

In my UIViewController's initWithNibNameOrNil, I call:

locationManager = [CLLocationManager new];

to create the object, then later in viewDidAppear I call:

[locationManager startUpdatingLocation];
[locationManager startUpdatingHeading];

But I never receive a location update; the location manager never starts updating the location. Yet if I replace that same line the initWithNibNameOrNil with:

locationManager = [[myAppDelegate appDelegate] locationManager];

everything works great, except for random crashes sometimes when

locationManager.delegate = self;

is set in the very next line after location manager is set to the already allocated manager in the app delegate. None of this makes sense to me; I don't understand why one is different from the other, much less why neither of them works consistently. Can someone please enlighten me?

Summary:

Method 1 (does not work):

In MapView.m:

initWithNibNameOrNil:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if(self) {
        locationManager = [CLLocationManager new];
        locationManager.delegate = self;
        locationManager.distanceFilter = kCLHeadingFilterNone;
        locationManager.headingFilter = 3;

        if(([[UIDevice currentDevice] batteryState] == UIDeviceBatteryStateCharging) || ([[UIDevice currentDevice] batteryState] == UIDeviceBatteryStateFull))
            locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
        else
            locationManager.desiredAccuracy = kCLLocationAccuracyBest;

        //more setup irrelevant to the question
    }
    return self;
}

viewDidAppear:

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:YES];
    [locationManager startUpdatingLocation];
    [locationManager startUpdatingHeading];
}

Notes: Using this method of creating the location manager, location services never get enabled after startUpdatingLocation is called in viewDidAppear and so no location updates are ever received.


Method 2 (does work, mostly):

In myAppDelegate.m:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [[UIDevice currentDevice] setBatteryMonitoringEnabled:YES];

    self.locationManager = [CLLocationManager new];
    self.locationManager.distanceFilter = kCLHeadingFilterNone;
    self.locationManager.headingFilter = 3;

    if(([[UIDevice currentDevice] batteryState] == UIDeviceBatteryStateCharging) || ([[UIDevice currentDevice] batteryState] == UIDeviceBatteryStateFull))
        self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
    else
        self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;

    //more irrelevant setup
}

In MapView.m:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if(self) {
        locationManager = [[Trail_TrackerAppDelegate appDelegate] locationManager];
        locationManager.delegate = self;
        //more irrelevant setup
    }
    return self;
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:YES];
    [locationManager startUpdatingLocation];
    [locationManager startUpdatingHeading];
}

Notes: this allocation method works, except if I push MapView, then pop it, push it again, pop it again, then try to push it again, I get a crash every time at the line in initWithNib... where I set the delegate to self; NSZombieEnabled says:

-[CLLocationManager setDelegate:]: message sent to deallocated instance 0x9540ad0

Interesting...

NSLog(@"%s: %@; %@", PRETTY_FUNCTION, self, locationManager) says this:

-[MapView initWithNibName:bundle:]: <MapView: 0x92ae3e0>; <CLLocationManager: 0x92a3560>

Method 3 (works all-around):

In MapView.m:

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:YES];
    locationManager = [CLLocationManager new];
    locationManager.delegate = self;
    locationManager.distanceFilter = kCLHeadingFilterNone;
    locationManager.headingFilter = 3;

    if(([[UIDevice currentDevice] batteryState] == UIDeviceBatteryStateCharging) || ([[UIDevice currentDevice] batteryState] == UIDeviceBatteryStateFull))
        locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
    else
        locationManager.desiredAccuracy = kCLLocationAccuracyBest;

    [locationManager startUpdatingLocation];
    [locationManager startUpdatingHeading];
}

Notes: all of the allocation and setup is done in viewDidAppear, and I don't get any crashes after repeated pushing/popping of the view controller. This is great, but I don't like allocating in viewDidAppear because the app lags for just a moment because (I think) the allocation of locationManager clogs up the main thread for that time. I just really don't understand why Method 1 doesn't work at all, Method 2 crashes, and Method 3 works. Don't they all do more-or-less the same thing?

Sorry for all the code, and congrats to anyone who made it all the way through this maze of a question! :)

Bhanu
  • 1,249
  • 10
  • 17
eric.mitchell
  • 8,817
  • 12
  • 54
  • 92
  • Add this log statement immediately after the call to [CLLocation new] and immediate before you ask to startUpdatingLocation: `NSLog("%s: %@; %@", __PRETTY_FUNCTION__, self, locationManager)` and then copy/paste the results into your question. – Firoze Lafeer Dec 11 '11 at 07:35
  • So it seems the code changed? Earlier you were doing [CLLocationManager new] in your init. Now it's in viewDidAppear, but it seems you're not setting the delegate now. – Firoze Lafeer Dec 11 '11 at 16:43
  • Yes, the code changed. [CLLocationManager new] in init had no effect, but putting it in viewDidAppear *does* work. I accidentally omitted the line where I set the delegate when stripping down the method for brevity's sake. I'm posting a clear does work/doesn't work in the question now. – eric.mitchell Dec 11 '11 at 18:35
  • Can you add the logs to method 2? I believe you added them only to method 3. One of the logs should be in the init method – Firoze Lafeer Dec 11 '11 at 22:48
  • Pls add the log to both the init and the viewDidAppear for method 2. That's what we've been trying to compare. – Firoze Lafeer Dec 12 '11 at 01:30
  • The problem is gone- though I'm not especially sure why. The code in method 1 now works as it should. I'm not really sure what I changed to make it work... – eric.mitchell Dec 12 '11 at 01:47
  • I'll update with an answer once I figure out what I did to fix it – eric.mitchell Dec 12 '11 at 01:49
  • Well, that's the trouble with not following a systematic approach to debugging a problem. Sometimes you can fix it, have no idea why, and then a similar problem happens in the future and you still don't have any idea why. I think I have an idea of exactly what was happening here, but it's impossible to verify without proper logging. – Firoze Lafeer Dec 12 '11 at 01:50
  • What is your idea? I might be able to confirm it. – eric.mitchell Dec 12 '11 at 02:08

3 Answers3

1

Try:

locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
[self.locationManager startUpdatingLocation];

instead of new. Are you sure you are assigning the delegate as self? Otherwise your class won't listen for location updates.

Bani Uppal
  • 866
  • 9
  • 17
  • I'm 100% sure. The *only* line I change is locationManager = [CLLocationManager new]; to locationManager = [[myAppDelegate appDelegate] locationManager]; – eric.mitchell Dec 11 '11 at 05:50
  • Changing to [[CLLocationManager alloc] init] has the same effect as [CLLocationManager new] - nothing happens when startUpdatingLocation is called – eric.mitchell Dec 11 '11 at 05:54
1

Turns out the culprit was a seemingly-innocent piece of code that was turning off the location manager as soon as it was being started. The only reason I saw it was because I compared the project to a recent backup, and noticed the difference. Another problem solved because of backing up!

eric.mitchell
  • 8,817
  • 12
  • 54
  • 92
0

Careful where you're defining your location manager. When using ARC, it could happen that your location manager instance gets discarded.

leviathan
  • 11,080
  • 5
  • 42
  • 40