MKMapView with multiple overlay memory issues

There seems to be a "problem" with overlays and MapKit . Unlike annotations, overlays are not reused, so adding multiple overlays may cause memory problems on a real device. I have had this problem several times. So my question is, how can I reuse MKOverlay and improve MapKit overlay MapKit ?

+4
source share
1 answer

The answer to this is not "reuse", but in order to draw everything in one MKOverlayView , and then draw it on the map.

Several MKPolygons , MKOverlays , etc. cause heavy memory usage when drawing on the map. This is because it does NOT reuse overlays on MapKit . However, in the reuseWithIdentifier annotations, there are reuseWithIdentifier overlays. Each overlay creates a new layer as an MKOverlayView on a map with an overlay in it. Thus, the use of memory will grow quite quickly, and the use of cards will become ... albeit sluggish, almost impossible.

Therefore, there is a workaround: instead of each individual overlay of each layer, you can add all MKOverlays to one MKOverlayView . This way you actually create only one MKOverlayView and therefore there is no need to reuse it.

This is a workaround, in this case for MKPolygons , but it should not be different for others, such as MKCircles , etc.

create class: MultiPolygon (subclass of NSObject )

in MultiPolygon.h :

 #import <MapKit/MapKit.h> //Add import MapKit @interface MultiPolygon : NSObject <MKOverlay> { NSArray *_polygons; MKMapRect _boundingMapRect; } - (id)initWithPolygons:(NSArray *)polygons; @property (nonatomic, readonly) NSArray *polygons; @end 

in MultiPolygon.m :

 @implementation MultiPolygon @synthesize polygons = _polygons; - (id)initWithPolygons:(NSArray *)polygons { if (self = [super init]) { _polygons = [polygons copy]; NSUInteger polyCount = [_polygons count]; if (polyCount) { _boundingMapRect = [[_polygons objectAtIndex:0] boundingMapRect]; NSUInteger i; for (i = 1; i < polyCount; i++) { _boundingMapRect = MKMapRectUnion(_boundingMapRect, [[_polygons objectAtIndex:i] boundingMapRect]); } } } return self; } - (MKMapRect)boundingMapRect { return _boundingMapRect; } - (CLLocationCoordinate2D)coordinate { return MKCoordinateForMapPoint(MKMapPointMake(MKMapRectGetMidX(_boundingMapRect), MKMapRectGetMidY(_boundingMapRect))); } @end 

Now create a class: MultiPolygonView (subclass of MKOverlayPathView )

in MultiPolygonView.h :

 #import <MapKit/MapKit.h> @interface MultiPolygonView : MKOverlayPathView @end 

In MultiPolygonView.m :

 #import "MultiPolygon.h" //Add import "MultiPolygon.h" @implementation MultiPolygonView - (CGPathRef)polyPath:(MKPolygon *)polygon { MKMapPoint *points = [polygon points]; NSUInteger pointCount = [polygon pointCount]; NSUInteger i; if (pointCount < 3) return NULL; CGMutablePathRef path = CGPathCreateMutable(); for (MKPolygon *interiorPolygon in polygon.interiorPolygons) { CGPathRef interiorPath = [self polyPath:interiorPolygon]; CGPathAddPath(path, NULL, interiorPath); CGPathRelease(interiorPath); } CGPoint relativePoint = [self pointForMapPoint:points[0]]; CGPathMoveToPoint(path, NULL, relativePoint.x, relativePoint.y); for (i = 1; i < pointCount; i++) { relativePoint = [self pointForMapPoint:points[i]]; CGPathAddLineToPoint(path, NULL, relativePoint.x, relativePoint.y); } return path; } - (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context { MultiPolygon *multiPolygon = (MultiPolygon *)self.overlay; for (MKPolygon *polygon in multiPolygon.polygons) { CGPathRef path = [self polyPath:polygon]; if (path) { [self applyFillPropertiesToContext:context atZoomScale:zoomScale]; CGContextBeginPath(context); CGContextAddPath(context, path); CGContextDrawPath(context, kCGPathEOFill); [self applyStrokePropertiesToContext:context atZoomScale:zoomScale]; CGContextBeginPath(context); CGContextAddPath(context, path); CGContextStrokePath(context); CGPathRelease(path); } } } @end 

We need to import MultiPolygon.h and MultiPolygonView.h into your ViewController

Create one polygon from all: As an example, I have an array with polygons: polygonsInArray .

 MultiPolygon *allPolygonsInOne = [[MultiPolygon alloc] initWithPolygons:polygonsInArray]; 

Add allPolygonsInOne to mapView:

 [mapView addOverlay:allPolygonsInOne]; 

Also change your viewForOverlay method:

 -(MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay { if ([overlay isKindOfClass:[MultiPolygon class]]) { MultiPolygonView *polygonsView = [[MultiPolygonView alloc] initWithOverlay:(MultiPolygon*)overlay]; polygonsView.fillColor = [[UIColor magentaColor] colorWithAlphaComponent:0.8]; polygonsView.strokeColor = [[UIColor blueColor] colorWithAlphaComponent:0.8]; polygonsView.lineWidth = 1; return polygonsView; } else { return nil; } } 

And this greatly reduced memory usage for several overlays on mapView . Now you are not reusing it because only one OverlayView . Therefore, there is no need to reuse.

+11
source

Source: https://habr.com/ru/post/1491616/


All Articles