5

I have followed the advice available in several SO questions, like this one, in order to release MKMapView from memory - my code below

- (void)viewDidDisappear:(BOOL)animated {

    [super viewDidDisappear:animated];

    self.map.mapType = MKMapTypeHybrid;
    self.map.showsUserLocation = NO;
    self.map.delegate = nil;
    [self.map removeFromSuperview];
    self.map = nil;

    self.locationManager.delegate = nil;
}

In part, it works, but not entirely. Let me provide some data.

Below is the memory allocation recording from Instruments.

enter image description here

The two red flags (Generations) indicate the states before I displayed MKMapViewin a modal view controller and after I have dismissed it. MKMapView seems to get deallocated. For instance, if I filter Statistics stack in Instruments for MKMapView, the object does indeed appear when the modal view is presented, and disappears once it's closed. However, having dismissed the map view, I still have 30+ MB of memory that has not been freed up.

Generation B (second red flag) data shows that there is a large number of objects (and non-objects) that are holding this memory.

enter image description here

When I look at extended details of one of those instances, it usually shows a stack trace that features private classes that, I guess, are related to map drawing enter image description here

Does anyone know how to free up all that data? Is there some cache I could/should clean?

artooras
  • 6,315
  • 9
  • 45
  • 78

2 Answers2

1

In my app, which uses the map view controller under control of a tab view controller, I store a reference to a MKMapView in a static variable and use this same map view over and over again instead of allocating a new one in ViewDidLoad every time. My (partial) code:

@implementation PubMapViewController {
    NSMutableArray *annotations;
}
static MKMapView *_mapView = nil;

- (void)viewDidLoad {
    [super viewDidLoad];
    if (!_mapView) {
        _mapView = [[MKMapView alloc] init]; // frame set up with constraints
    } else {
        [_mapView removeAnnotations:_mapView.annotations]; // precaution
    }
    [_mapViewProxy addSubview:_mapView];
    [self addConstraints:_mapView];
    [self configureView:((PubTabBarController *)self.tabBarController).detailItem];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [_mapView addAnnotations:annotations];
    if (annotations.count == 1) {
        [_mapView selectAnnotation:annotations[0] animated:YES];
    }
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    [_mapView removeAnnotations:_mapView.annotations];
}

Here, configureView: sets up the map for self.tabBarController.detailItem, sets its delegate and stores the map annotations in variable annotations.

The map is made a subview to a view defined in the interface builder (instance variable @property (weak, nonatomic) IBOutlet UIView *mapViewProxy;). The map must obtain the same size as mapViewProxy, and as I use autolayout, the frame size of _mapView is controlled entirely using the constraints set up in addConstraints (top, bottom, left, and right equal to _mapView.superview).

I found it compulsory to remove the annotations from the map in viewDidDisppear: and to add them back in viewDidAppear. It might be even more clean to unset _mapView.delegate in viewDidDisppear: and set it back in viewDidAppear.

BTW: The static variable _mapView still misleadingly carries the leading underscore since it was an instance variable before set up by defining the MKMapView in IB.

vilmoskörte
  • 291
  • 1
  • 10
0

This is the solution that I used and it is working fine. I think this problem was only introduced recently because I haven't had issues with the Map deallocating properly before within the same project.

I stored the map instance within a singleton and simply check for it's existence before creating a new one. ie:

if let existingMapView = LocationSingleton.sharedInstance.singletonMapView {

    //Display map
}else{

    let newMapView = //Instantiate new map view controller
    LocationSingleton.sharedInstance.singletonMapView = newMapView

    //Display map
}
PJeremyMalouf
  • 613
  • 6
  • 15