I was implementing a location services functionality when I (luckily) noticed that my class wasn’t deallocating. I suspected my own code, of course, but a little bit of digging showed that the problem actually seemed to be in CLLocationManager, specifically it seems to retain the delegate after calling requestLocation() or startUpdatingLocation().

Test Playground

I can easily reproduce it every time and everything seems to be pointing to the delegate being retained while it shouldn’t since it’s marked as weak. Example:

class TestClass: NSObject, CLLocationManagerDelegate {
    let locationManager = CLLocationManager()

    init(request: Bool) {
        super.init()
        locationManager.delegate = self
        if request {
            locationManager.requestLocation()
            // or locationManager.startUpdatingLocation()
        }
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {}
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {}
}

weak var obj1: TestClass?
weak var obj2: TestClass?

func createInstances() {
    let instance1 = TestClass(request: true)
    obj1 = instance1

    let instance2 = TestClass(request: false)
    obj2 = instance2
}

createInstances()

print("obj1 deallocated: \(obj1 == nil)")
print("obj2 deallocated: \(obj2 == nil)")

This class creates a CLLocationManager, sets the delegate to self and requests location (if we tell it too). Now, simply testing 2 instances of this class (one of which doesn’t request location) shows that obj1 is not deallocated while obj2 is. I’ve filled a bug with Apple (FB8943197) so hopefully we can get this resolved soon as it can cause pretty nasty memory leaks.