问题
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?
回答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.
回答2:
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
}
来源:https://stackoverflow.com/questions/28633032/mkmapview-release-memory