7

Hi I'm playing around with locations on iPhone and right from the start I ran into problems. I've localized my problem and determined it's CLLocationManager that's bugging me.

So I developed very simple application. I just have a view controller with a CLLocationManager in it. On view did load I initialize CLLocationManager and start updating. I've also implemented two methods didFailWithError and didUpdateToLocation.

I've read a lot of questions and what i have learned so fare is this. You have to retain CLLocationManager during initialization. Also it's wise to set CLLocationManagers delegate to nil during unloading of a view (something to do with messages passing to CLLocationManager because framework retains it and it's never properly release)

All in all I just cant find a decent explanation on what to do and how to make it work.

Here's my code so if anybody could figure it out I would appreciate it. viewControllers header file

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>

@interface CoreLocationViewController : UIViewController <CLLocationManagerDelegate>
{
    CLLocationManager *locManager;
}

@property (nonatomic, retain) CLLocationManager *locManager;

@end

viewController .m file

#import "CoreLocationViewController.h"


@implementation CoreLocationViewController
@synthesize locManager;

- (void)viewDidLoad 
{
    [super viewDidLoad];

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

    self.view.backgroundColor = [UIColor grayColor];
}

#pragma mark -
#pragma mark Location Delegate

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
        NSLog(@"in fail with error");
}

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
    NSLog(@"in update to location");
}


#pragma mark -
#pragma mark Memory Management

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

- (void)viewDidUnload 
{
self.locManager.delegate = nil;
    [super viewDidUnload];
}


- (void)dealloc 
{
    [locManager release];
        [super dealloc];
}


@end

BTW: I'm using iOS 4.2. I'm pointing this out because I read that Apple has changed location delegates in iOS 4

Girish
  • 4,692
  • 4
  • 35
  • 55
paxx
  • 1,079
  • 2
  • 12
  • 26
  • 2
    You are leaking here, because the self.locManager = [[CLLocationManager alloc] init]; is retaining the object on top of the ref count you get from using alloc init. – logancautrell May 12 '11 at 18:32

9 Answers9

23

In my case, the delegate never gets called because of threading problem

  • the thread that operates LocationManager must have an NSRunLoop set up
  • if you use main thread, you already have a runloop, now you just need to instantiate the manager and use it on main thread

Read this to know more about CLLocationManagerDelegate

Bùi Thanh Hải
  • 586
  • 3
  • 10
  • 1
    Perfect. Fixed it for me. I already tried this earlier, but only ensured it for the `startUpdatingLocation` method. Actually, the alloc/init etc has to be on the main thread as well! – Tim Bodeit Nov 13 '13 at 23:32
  • 1
    The answer! Instantiating the CLLocationManager on a separate queue from the main thread was my problem. Dispatching the call to the main thread fixed it. – jcady Jan 19 '16 at 20:26
  • 1
    @TimBodeit This certainly fixed the issue for me. But what I don't understand is previously I had it called from `viewDidLoad`. Isn't `viewDidLoad` being ran from main thread? – mfaani Jul 17 '17 at 17:04
8

Furthermore in iOS8 you must have two extra things:

  • Add a key to your Info.plist and request authorization from the location manager asking it to start.

    • NSLocationWhenInUseUsageDescription

    • NSLocationAlwaysUsageDescription

  • You need to request authorization for the corresponding location method.

    • [self.locationManager requestWhenInUseAuthorization]

    • [self.locationManager requestAlwaysAuthorization]

Code example:

self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
// Check for iOS 8. Without this guard the code will crash with "unknown selector" on iOS 7.
if ([self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
    [self.locationManager requestWhenInUseAuthorization];
}
[self.locationManager startUpdatingLocation];

Source: http://nevan.net/2014/09/core-location-manager-changes-in-ios-8/

MarMass
  • 5,755
  • 1
  • 18
  • 15
4

Other posters have mentioned checking for authorization, and you should definitely add that, but I do not think that is your problem. If you were not authorized your didFailWithError: method would be called. This leads to believe something else is going on here.

It may be a good idea to make sure that your location manager is indeed being allocated and while your at it you can fix your memory leak.

Try this:

CLLocationManager* lm = [[CLLocationManager alloc] init];
NSAssert(lm != nil, @"Failed to allocate CLLocation manage");
self.locManager = lm;
[lm release];
self.locManager.delegate = self;
[self.locManager startUpdatingLocation];
// Let's just print what we got
NSLog(@"CLLocationManager is %@", lm);

If your run this your will get one of the following results:

1) if the location manager is returning nil, your program will crash into the debugger (because of NSAssert)

2) if "CLLocationManager is ...." prints and still you see no updates then you have a real mystery on your hands.

3) Nothing prints. That would mean, perhaps due to something incorrectly linked up in Interface Builder, that viewDidLoad is not being called at all.

4) You will get a didFailWithError: call because you are not authorized. Which means that at least everything is working as expected.

As an aside you had a memory leak because you were assigning the result of an alloc directly to a property with the retain attribute. After alloc your count would be +1 after assignment it would +2, so if the view was unloaded and reloaded you would leak.

Hope some of this helps.

idz
  • 12,825
  • 1
  • 29
  • 40
  • tried it.. and i got a fishy CLLocationManager is .. i think there's something more to it.. maybe locationFramework on my computer isn't installed properly.. do u know of any projects, or do u have and project that uses locationManager and works 100%.. i wanna test if it's my code or maybe something else.. btw: tnx for alloc thing.. did not know that.. used to be a .NET coder so i'm still trying to adapt to no garbage collector environment – paxx May 11 '11 at 16:20
  • i'm using a simulator because i don't have my own developers acc.. but i'm gonna try it on friends acc today to see what happens on device.. but i'm confused in one thing.. shouldn't it at least call an error delegate on simulator? or is it normal that on simulator nothing happens??? – paxx May 12 '11 at 07:37
  • @paki In my version of the simulator, 4.2, it does call out correctly (although you only get a few updates). I do not recall what happened in earlier versions. – idz May 12 '11 at 07:52
3

This may be nit picky but in viewDidLoad change:

locManager.delegate = self;

to:

self.locManager.delegate = self;

I don't know if it will fix it, but is best to be consistent.

dredful
  • 4,378
  • 2
  • 35
  • 54
3

So I managed to find a bug. It was in memory management. In my app delegate file I initialized a CoreLocationViewController and added it as a subview to my window, after that I released it and all is well. But, the releasing part was not a good way to go. After I released my ViewController, dealloc method gets called and it releases locationManager.

So the proper thing to do is not to release a ViewController that holds locationManager, or find another way of dealing with it. I'm not quite sure why that was a problem because I thought that after adding a ViewController to a window it gets retained, therefore its localtionManager gets retained as well. If anybody can clear it up for me it would be much appreciated.

dredful
  • 4,378
  • 2
  • 35
  • 54
paxx
  • 1,079
  • 2
  • 12
  • 26
  • No. After adding a view controller's view to a window its VIEW gets retained, but not the view itself. This is an old post, but still worth commenting on. Releasing a view controller that's on-screen will likely cause a crash when anything that happens (like a button tap) tries to call one of the methods of the view controller. – Duncan C Nov 17 '13 at 15:14
2

are you testing on device? are location services enabled?

if (self.locManager.locationServicesEnabled){
//do domething
}
Nik Burns
  • 3,363
  • 2
  • 29
  • 37
0

I've got the same problem and as paxx said, it's a memory management problem. My solution is retain 'self' manually. As the code shows below:

- (void) viewDidload 
{
    [super viewDidload];
    if (![CLLocationManager locationServicesEnabled]) {
        NSLog(@"Location Service Disabled!");
    } else {
        [self retain];
        //initialize the cllocationmanager obj.
    }
}


-(void)locationManager:(CLLocationManager *)manager 
    didUpdateLocations:(NSArray *)locations
{
    //do your surf
    [self release];
}


-(void)locationManager:(CLLocationManager *)manager 
   didUpdateToLocation:(CLLocation *)newLocation 
          fromLocation:(CLLocation *)oldLocation
{
    //do your stuff
    [self release];
}

-(void)locationManager:(CLLocationManager *)manager 
      didFailWithError:(NSError *)error
{
    //do your stuff
    [self release];
}

Not pretty, but works fine for me.

MasterBeta
  • 606
  • 6
  • 15
0

Idk if it is a matter but in my code I also setup the desired accuracy

[locManager setDesiredAccuracy:kCLLocationAccuracyBest];

For example.

Arcantos
  • 1,052
  • 1
  • 13
  • 28
0

Try this:

 NSLog(@"location services enabled: %d", [locManager locationServicesEnabled]);

From the docs:

The user can enable or disable location services altogether from the Settings application by toggling the switch in Settings > General > Location Services.

Rayfleck
  • 12,116
  • 8
  • 48
  • 74
  • tried it.. i used `if ([CLLocationManager locationServicesEnabled]) { [self.locManager startUpdatingLocation]; }` but it didn't help. I think there's something more to it – paxx May 11 '11 at 16:16
  • What did the NSLog statement say? Are your loc services enabled or not? The if statement will not enable them if they're disabled - you have to do that in the Settings app. – Rayfleck May 11 '11 at 22:20
  • service is enabled.. but i'm running on simulator, and i'm not sure does running on simulator call any of the delegates.. i guess it should at least call error delegate – paxx May 12 '11 at 07:36
  • Yes, the simulator does GPS callbacks to the delegate. At this point, I'd do a clean and rebuild, and make sure you do IOS Simulator / Reset Content and Settings... to make sure something funky isn't cached. – Rayfleck May 12 '11 at 12:40