Potential memory leaks in CLLocationUpdate.Updates

This is my first post here. Please guide me, if I need to provide more information to answer this post.

I write a simple application, that monitors GPS position (location). I followed Apple documentation for LiveUpdates: https://developer.apple.com/documentation/corelocation/supporting-live-updates-in-swiftui-and-mac-catalyst-apps

My app can monitor location in foreground, background or it can completely stop monitoring location. Background location, if needed, is switched on when application changes scenePhase to .background. But it is in the foreground, that memory leaks occur (according to Instruments/Leaks. Namely Leaks points to the instruction:

let updates = CLLocationUpdate.liveUpdates()

every time I start location and then stop it, by setting updatesStarted to false.

Leaks claims there are 5x leaks there:

Malloc 32 Bytes	1	0x6000002c1d00	32 Bytes	libswiftDispatch.dylib OS_dispatch_queue.init(label:qos:attributes:autoreleaseFrequency:target:)

CLDispatchSilo	1	0x60000269e700	96 Bytes	CoreLocation 0x184525c64

Malloc 48 Bytes	1	0x600000c8f2d0	48 Bytes	Foundation +[NSString stringWithUTF8String:]

NSMutableSet	1	0x6000002c4240	32 Bytes	LocationSupport 0x18baa65d4

dispatch_queue_t (serial)	1	0x600002c69c80	128 Bytes	libswiftDispatch.dylib OS_dispatch_queue.init(label:qos:attributes:autoreleaseFrequency:target:)

I tried [weak self] in Task, but it doesn't solve the leaks problem and causes other issues, so I dropped it. Anyway, Apple doesn't use it either.

Just in case this is my function, which has been slightly changed comparing to Apple example, to suit my needs:

func startLocationUpdates() {
        Task() {
            do {
                self.updatesStarted = true
                let updates = CLLocationUpdate.liveUpdates()
                for try await update in updates {
                    // End location updates by breaking out of the loop.
                    if !self.updatesStarted {
                        self.location = nil
                        self.mapLocation = nil
                        self.track.removeAll()
                        break
                    }
                    
                    if let loc = update.location {
                        let locationCoordinate = loc.coordinate
                        let location2D = CLLocationCoordinate2D(latitude: locationCoordinate.latitude, longitude: locationCoordinate.longitude)
                        self.location = location2D
                        if self.isAnchor {
                            if #available(iOS 18.0, *) {
                                if !update.stationary {
                                    self.track.append(location2D)
                                }
                            } else {
                                // Fallback on earlier versions
                                if !update.isStationary {
                                    self.track.append(location2D)
                                }
                            }
                        }
                     }
                }
            } catch {
		//
            }
            return
        }
    }

Can anyone help me locating these leaks?

Thanks so much for sharing the memory leak issue.

Have you tried incorporating the Instruments memory leak template to pinpoint the exact location of the leaks? Is that the output objects I see in your post?

Malloc 32 Bytes	1	0x6000002c1d00	32 Bytes	libswiftDispatch.dylib OS_dispatch_queue.init(label:qos:attributes:autoreleaseFrequency:target:)

CLDispatchSilo	1	0x60000269e700	96 Bytes	CoreLocation 0x184525c64

Malloc 48 Bytes	1	0x600000c8f2d0	48 Bytes	Foundation +[NSString stringWithUTF8String:]

NSMutableSet	1	0x6000002c4240	32 Bytes	LocationSupport 0x18baa65d4

dispatch_queue_t (serial)	1	0x600002c69c80	128 Bytes	libswiftDispatch.dylib OS_dispatch_queue.init(label:qos:attributes:autoreleaseFrequency:target:)

Understandably, it's more helpful to see firsthand, so would it be possible to provide a simple, focused project involving just the relevant code for reproducing the problem?

Do you get the same results with just the relevant code in a small test project? If so, please share a link to your test project. That'll help us better understand what's going on. If you're not familiar with preparing a test project, take a look at Creating a test project.

Even a small structure can be super valuable, as it allows us to quickly reproduce the scenario and identify the root cause.

Thanks.

Thanks for your swift reply. Sorry for small delay on my side. I have been preparing the so called Test Project (see (2) below)

  1. Correct - the leaks I attached come from the Instruments / Leaks tool.

  2. I am not sure what you exactly mean by "Test project". I assumed you wanted to have a new, simple project containing the core functionality to reproduce the leaks issue. I created such basic-functionality project, stripping off all unnecessary elements and code, just leaving the core: location tracking. I kept the structure of my app though (so it is not as simple as it could be for such functionality), as it might help to investigate the case, I think. And finally YES, I observe the same leaks in this test project as in my main project - 5 leaks every time I press "<back" button, which stops location tracking. In the Leak tool, when I choose any of the leaks, and then choose in the right panel the bold marked Stack trace "closure #1 in LocationsHandler.startLocationUpdates()" I get this code marked:

                    Task() {
                           do {
                                       self.updatesStarted = true
5x 336 bytes                let updates = CLLocationUpdate.liveUpdates()
                            for try await update in updates {
  1. I do not know how to share the link to my project though.

Since WatchOS11 I also experience this. Only by setting the newLocation to my Published var location will cause the leak.

inline-code func update(newLocation: CLLocation){ // print("new location (location)")

    if (self.locationFix == false) {
        self.locationFix = true
    }
    
    location = newLocation

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
            if let location = locations.last {
                self.update(newLocation: location)
            }
        }
Potential memory leaks in CLLocationUpdate.Updates
 
 
Q