Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stop iOS 7 MKMapView from leaking memory

I've noticed that my app leaks memory, but if I take the MKMapView out the memory problem goes away.

To test the theory, I made a dead simple project that has a view that pushes a view with a MKMapView in it and pops and pushes. Nothing more. No code in the view controllers, everthing done via storyboard.

If I go back and forth to the map view, it starts about 3MB after doing pushing and popping the view with the map in it this about 15 times the memory is around 230MB.

Anyone else seen this? Seems like a pretty big bug. Is there a different way to use MKMapView that will prevent it from leaking so much?

enter image description here

like image 359
codercat Avatar asked Nov 22 '13 06:11

codercat


People also ask

What causes memory leaks iOS?

A memory leak occurs when a given memory space cannot be recovered by the ARC (Automatic Reference Count) because it is unable to tell if this memory space is actually in use or not . One of the most common problems that generate memory leaks in iOS is retained cycles we will see it later.

How do you find and resolve memory leaks in iOS?

Xcode Instruments is a great tool for finding potential memory leaks but sometimes it's not easy to identify the problem. We also need to run it every time to check for a potential leak and monitor the memory usage. We can use another technique to continuously monitor for potential memory leaks.

What causes memory leaking?

A memory leak starts when a program requests a chunk of memory from the operating system for itself and its data. As a program operates, it sometimes needs more memory and makes an additional request.


3 Answers

I had faced the same issue and (thanks to Stackoverflow) fixed it by changing MKMapType in viewWillDisappear and deallocating/setting its delegate to nil.As it still sends message to delegates. This is documented in MKMapViewDelegate Protocol Reference:

Before releasing an MKMapView object for which you have set a delegate, remember to set that object’s delegate property to nil. One place you can do this is in the dealloc method where you dispose of the map view

.

-(void)viewWillDisappear:(BOOL)animated{
  [super viewWillDisappear:animated];
  [self applyMapViewMemoryFix];

}

- (void)applyMapViewMemoryFix{

switch (self.mkMapView.mapType) {
    case MKMapTypeHybrid:
    {
        self.mkMapView.mapType = MKMapTypeStandard;
    }

        break;
    case MKMapTypeStandard:
    {
        self.mkMapView.mapType = MKMapTypeHybrid;
    }

        break;
    default:
        break;
}
self.mkMapView.showsUserLocation = NO;
self.mkMapView.delegate = nil;
[self.mkMapView removeFromSuperview];
self.mkMapView = nil;
}

hope this helps

like image 157
iAhmed Avatar answered Oct 11 '22 14:10

iAhmed


Swift Version:

override func viewWillDisappear(_ animated:Bool) {
    super.viewWillDisappear(animated)
    self.applyMapViewMemoryFix()
}

func applyMapViewMemoryFix() {
    switch (self.mapView.mapType) {
    case MKMapType.hybrid:
        self.mapView.mapType = MKMapType.standard
    case MKMapType.standard:
        self.mapView.mapType = MKMapType.hybrid
    default:
        break
    }
    self.mapView.showsUserLocation = false
    self.mapView.delegate = nil
    self.mapView.removeFromSuperview()
    self.mapView = nil
}
like image 32
fede1608 Avatar answered Oct 11 '22 15:10

fede1608


The best solution I have found is to have an instance of MKMapView in your delegate, you will allocate it only once.

Then anytime you need a MapView, you just use the one from the delegate.

In my case i needed to clean the annotations from it as soon as the view willDisappear ( to not have older annotations on the map ).

  - (void)viewDidLoad {
      AppDelegate *delegate = [UIApplication sharedApplication].delegate;
          if (!delegate.appModel.mapView)
             delegate.appModel.mapView = [[MKMapView alloc] initWithFrame:self.view.frame];
      self.mapView = delegate.appModel.mapView;
      [self.mapView setFrame:self.view.frame];
      [self.mapView setDelegate:self];
      [self.view addSubview:self.mapView];
   }

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [self.mapView removeAnnotations:self.mapView.annotations];
    for (id<MKOverlay> overlay in self.mapView.overlays) {
        [self.mapView removeOverlay:overlay];
    }
}
like image 38
xGoPox Avatar answered Oct 11 '22 14:10

xGoPox