问题
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! :)
回答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.
回答2:
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!
回答3:
Careful where you're defining your location manager. When using ARC, it could happen that your location manager instance gets discarded.
来源:https://stackoverflow.com/questions/8462146/cant-allocate-cllocationmanager