Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MKMapView release memory

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?

like image 350
artooras Avatar asked Nov 10 '22 18:11

artooras


1 Answers

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.

like image 120
vilmoskörte Avatar answered Nov 14 '22 23:11

vilmoskörte