GarySabo
GarySabo

Reputation: 6680

"Failure to deallocate CLLocationManager on the same runloop as its creation may result in a crash"

I'm seeing this error in the console and not sure what the cause is? I'm requesting user location once.

2018-09-12 20:04:26.912292-0400 Watch Extension[40984:3250895] [Client] Failure to deallocate CLLocationManager on the same runloop as its creation may result in a crash

Code below:

import CoreLocation


class WorkoutLocationManager: NSObject, CLLocationManagerDelegate {

    private var locationManager: CLLocationManager?
    public var formattedWorkoutAddress: String?

    public func getWorkoutLocation() {
        guard CLLocationManager.locationServicesEnabled() else {
            print("User does not have location services enabled")
            return
        }

        locationManager = CLLocationManager()
        locationManager?.delegate = self
        locationManager?.desiredAccuracy = kCLLocationAccuracyNearestTenMeters

        let locationAuthorizationStatus = CLLocationManager.authorizationStatus()

        switch locationAuthorizationStatus {
        case .authorizedAlways:
            print("location authorized Always")
            locationManager?.requestLocation()
        case .authorizedWhenInUse:
            print("location authorized When in Use")
            locationManager?.requestLocation()
        case .denied:
            print("location authorization denied")
        case .notDetermined:
            print("location authorization not determined")

        case .restricted:
            print("location authorization restricted")

        }

    }


    // MARK: - CLLocationManagerDelegate

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

                guard let currentLocation = locations.first else { return }

                let geocoder = CLGeocoder()
                geocoder.reverseGeocodeLocation(currentLocation) { (placemarksArray, error) in

                    if let unwrappedError = error  {
                        print("Geocoder error: \(unwrappedError)")
                    }

                    guard let placemarksArrayUnwrapped = placemarksArray else  { return }

                    if placemarksArrayUnwrapped.count > 0 {

                        if let placemark = placemarksArray?.first {

                            let name = placemark.name ?? ""
                            let subLocality = placemark.subLocality ?? ""
                            let locality = placemark.locality ?? ""
                            let state = placemark.administrativeArea ?? ""

//                            print("address1:", placemark.thoroughfare ?? "")
//                            print("address2:", placemark.subThoroughfare ?? "")
//                            print("city:",     placemark.locality ?? "")
//                            print("state:",    placemark.administrativeArea ?? "")
//                            print("zip code:", placemark.postalCode ?? "")
//                            print("country:",  placemark.country ?? "")

                            let workoutLocationAsString = (name + " " + subLocality + " " + locality + " " + state)
                            print("workoutLocationAsString = \(workoutLocationAsString)")
                            self.formattedWorkoutAddress = workoutLocationAsString

                        } else { print("no placemark")}
                    } else { print("placemark.count = 0")}
                }
    }

    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("location manager error = \(error)")
    }

}

usage:

private func handleWorkoutSessionState(didChangeTo toState: HKWorkoutSessionState,
                                           from fromState: HKWorkoutSessionState) {
        switch (fromState, toState) {
        case (.notStarted, .running):
            workoutLocationManager.getWorkoutLocation() 

Upvotes: 4

Views: 1935

Answers (1)

Rodrigo Morbach
Rodrigo Morbach

Reputation: 388

CLLocationManager must be created in a run loop. If you don't do that, you will receive the following warning from CoreLocation:

A location manager was created on a dispatch queue executing on a thread other than the main thread. It is the developer's responsibility to ensure that there is a run loop running on the thread on which the location manager object is allocated. In particular, creating location managers in arbitrary dispatch queues (not attached to the main queue) is not supported and will result in callbacks not being received.

In your case, it looks like your locationManager it's being created in the main thread, but an instance of WorkoutLocationManager is being used on some other thread, which causes it to be deallocated in this thread, and consequently, deallocating locationManager in the very same thread.

One option is to make sure that your instance of WorkoutLocationManager will be created and used only in the main thread.

Another option, that I haven't fully tested, is to try forcing the creation and deallocation of locationManager in the main thread, like so:

class WorkoutLocationManager: NSObject {

    var locationManager: CLLocationManager!

    override init() {
        super.init()
        self.performSelector(onMainThread: #selector(initLocationManager), with: nil, waitUntilDone: true)
    }

    @objc private func initLocationManager() {
        self.locationManager = CLLocationManager()
        self.locationManager.delegate = self
    }

    @objc private func deinitLocationManager() {
        locationManager = nil
    }

    deinit {
        self.performSelector(onMainThread: #selector(deinitLocationManager), with: nil, waitUntilDone: true)
    }

}

Let me know what you think about it.

Upvotes: 5

Related Questions