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.
The two red flags (Generations) indicate the states before I displayed MKMapView
in 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.
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
Does anyone know how to free up all that data? Is there some cache I could/should clean?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With