I have been battling with this problem for a while, and I think I've finally gotten to the bottom of it..
The reason that the location service doesn't stop when you ask it to is not because you haven't stopped it or released it properly. It's actually caused by releasing and re-allocating the CLLocationManager itself, in my experience.
If you have code which releases your CLLocationManager in applicationDidEnterBackground, and then you allocate a brand new one in applicationDidEnterForeground, etc., then you'll probably have this problem.
The solution is this:
- Only create your CLLocationManager object once, in applicationDidFinishLaunching.
- To start, call startUpdatingLocation, startMonitoringSignificantLocationChanges etc. as normal.
- To stop updates, call the appropriate stopUpdatingLocation, stopMonitoringSignificantLocationChanges etc. as normal.
- Never, ever release your CLLocationManager or set its' reference to nil (except possibly in applicationWillTerminate, but that probably won't make any difference).
This way, I went from having my app continue to use location services for up to 12 hours after putting my app away in the background, to the location services arrow disappearing within 10 seconds of backgrounding with this new approach.
Note: Tested on iPhone 4S running iOS 5.1.1. To get accurate results on your app's performance in this regard, make sure you go into Settings->Location Services->System Services and turn off the Status Bar Icon switch. That way, the status bar arrow will accurately reflect usage by apps alone.