MapView remove polyline overlay, memory leak on IOS 14.2

I need to remove a Polyline and replace it by a new one on my mapView about every 2 seconds. The Polyline is correctly removed and added to the screen so that is great. But I see the app memory use increase by about 5 mb per minute. This results in the app becoming very slow and unresponsive. I see the problem on the IOS 14.2 simulator and hardware (iPhone and iPad). On my 13.4 simulator this problem does occur and all works fine without memory leaking. Below run the the steps to reproduce in a loop:

1
create a regular polyLine with about 50 entries
polyLine.title = "polyLine"
2
mapView.addOverlay(PolyLine)
3
let allOverlays = mapView.overlays
for overlay in allOverlays {
if (overlay.title == "polyLine") {
    mapView.removeOverlay(overlay)
  }
}
4
mapView.addOverlay(PolyLine)

Note: I see the same problem when navigation routes (that is also using Polyline overlay) are recreated. So it looks like MapView graphically works ok but is not releasing memory after mapView.removeOverlay(overlay) is called.

Here is some output from instruments/leaks pointing to VectorKit:
MTLSimTexture 4048 < multiple > 1,48 MiB MTLSimDriver _77-[MTLSimTexture newTextureViewWithPixelFormatInternal:isInternalTextureView:]block_invoke
Malloc 16 Bytes 4083 < multiple > 63,80 KiB VectorKit geo::MallocZoneAllocator::allocate(unsigned long, unsigned long)
Malloc 144 Bytes 4105 < multiple > 577,27 KiB VectorKit geo::MallocZoneAllocator::allocate(unsigned long, unsigned long)

Replies

Update:

I noticed in instruments that the leak already occurs when adding the polyline overlay. So it does not happen on the mapView.removeOverlay(overlay) but on the mapView.addAnnotation(polyLine)

So my question is:
  • Has anybody noticed the problem too?

  • Is there a workaround ?


Note: my app becomes unresponsive after a few hours because it runs out of memory.
I noticed, by running on various simulators, IOS 13.7 does not have the problem but 14.0, 14.1 and 14.2 do.

here is the code to reproduce

//
// ViewController.swift
// ios14PolylineMemoryLeak
//
// Created by Henri on 26/11/2020.
//

import UIKit
import MapKit

class ViewController: UIViewController, MKMapViewDelegate {
   
  //
  // to see reproduction run instruments/leaks
  // - it will report leaks on IOS 14.2 and memory usage is increasing all the time.
  // - On IOS 13.7 all is ok, no memory leaks, and memory usage remains at a contant value
  //
  // Below out that Instruments Leaks is giving me.
  //  Malloc 144 Bytes  111  < multiple >  15,61 KiB
  //   VectorKit  geo::MallocZoneAllocator::allocate(unsigned long, unsigned long)
  //  MTLSimTexture  81  < multiple >  30,38 KiB
  //   MTLSimDriver_77-[MTLSimTexture newTextureViewWithPixelFormatInternal:isInternalTextureView:]blockinvoke
  //  Malloc 16 Bytes  93  < multiple >  1,45 KiB
  //   VectorKit  geo::MallocZoneAllocator::allocate(unsigned long, unsigned long)
  //
  //
   
  @IBOutlet weak var mapView: MKMapView!
   
  override func viewDidLoad() {
    super.viewDidLoad()
    setupMapView()
    reproduceIos14PolylineMemoryLeak()
  }
   
  func reproduceIos14PolylineMemoryLeak() {
    
= Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(fireTimer), userInfo: nil, repeats: true)
  }
   
  @objc func fireTimer() {
    let coordinate1 = mapView.convert(CGPoint(x: 100, y: 100), toCoordinateFrom: mapView)
    let coordinate2 = mapView.convert(CGPoint(x: 300, y: 300), toCoordinateFrom: mapView)
    let farawayCoordinate = locationWithBearing(bearing: 90, distanceMeters: 50000, origin: coordinate2)
    var coordinates = [CLLocationCoordinate2D]()
    coordinates.append(coordinate1)
    coordinates.append(coordinate2)
    coordinates.append(farawayCoordinate)
     
    let polyline = MKPolyline(coordinates: coordinates, count: coordinates.count)
     
    let overlays = mapView.overlays
    for overlay in overlays {
      mapView.removeOverlay(overlay)
    }
    mapView.addOverlay(polyline)

  }
   
  func locationWithBearing(bearing:Double, distanceMeters:Double, origin:CLLocationCoordinate2D) -> CLLocationCoordinate2D {
     
    let distRadians = distanceMeters / (6372797.6)
    let rbearing = bearing * Double.pi / 180.0
    let lat1 = origin.latitude * Double.pi / 180
    let lon1 = origin.longitude * Double.pi / 180
    let lat2 = asin(sin(lat1) * cos(distRadians) + cos(lat1) * sin(distRadians) * cos(rbearing))
    let lon2 = lon1 + atan2(sin(rbearing) * sin(distRadians) * cos(lat1), cos(distRadians) - sin(lat1) * sin(lat2))

    return CLLocationCoordinate2D(latitude: lat2 * 180 / Double.pi, longitude: lon2 * 180 / Double.pi)
  }
   
  func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
    if (overlay is MKPolyline)
    {
      let renderer = MKPolylineRenderer(overlay: overlay)
      renderer.strokeColor = .systemBlue
      renderer.lineWidth = 5.0
      return renderer
    }
    return MKOverlayRenderer(overlay: overlay)
  }
   
  func setupMapView() {
    let appleCoordinate = CLLocation(latitude: 37.33182, longitude: -122.03118)
    mapView.delegate = self
    let coordinateRegion = MKCoordinateRegion(
      center: CLLocationCoordinate2D(
        latitude: appleCoordinate.coordinate.latitude,
        longitude: appleCoordinate.coordinate.longitude),
      latitudinalMeters: 10 ,
      longitudinalMeters: 1000)
    mapView.setRegion(coordinateRegion, animated: true)
  }
}
I created a feedback report for this problem with number Nov 26, 2020 at 3:08 PM – FB8918120
Same problem here. I called addOverlay multiple times to track a user's route. Even though the poly line only had 2 points for each call, the memory link was obvious. Commented out the addOverlay, and no memory leak.
I checked with IOS14.3 after building with Xcode 12.3. The problem is not fixed yet.
Hi, I have read your question and the answers with great interest. I use polylines (ios 13.5 -> 14.3) to display my routes on the map. If I want to show a new route I delete the previous route. The polyline has then disappeared, but as soon as the new route is drawn, the old route reappears. So it is not possible to really remove a polyline or overlay. Is there a (temporary) solution for this?
I see this memory leak as well in iOS14.4. As stated above, it doesn't appear to leak in 13.7. Has anyone heard more information regarding a fix or workaround?
After a bit more digging, I believe this only affects the simulator. It seems to occur anytime an overlay on a map is rendered. When the map position changes for any reason, e.g. scrolling the map, the leak gets bigger. Obviously this becomes a more significant issue if the map position changes automatically as in Henrivb's case and mine. You can see the leak grow just by rendering one overlay on a map, then moving the map around. I've filed feedback to Apple on this as well.

Could you please try with the latest iOS 15 beta build and if the issue still persists please file a report via Feedback Assistant (https://feedbackassistant.apple.com/).

The issue still persists in iOS 15 and Xcode 13.1. Has anyone heard more information regarding a fix or workaround?