Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple MKOverlays on a MKMapView lead to memory warnings

I'm working on an iPhone app that shows a map with multiple circle overlays on certain locations. I'm running into serious memory issues and crashes when I add more than 6 circles and I zoom out far enough that they are all visible. When I zoom in such that only 2 circles are visible, all is fine. When I remove the MKOverlays, everything works fine.

Anyone who recognizes this behavior?

Code that creates the overlays. I store the overlays in a NSMutableDictionary for future reference (to be able to remove them from the map and to prevent double overlays)

- (void)updateMarkersForZones:(NSArray *)zones {
    NSLog(@"MapViewController: Update Markers");
    // For each zone, show a marker
    for (Zone* zone in zones) {
        NSString *keyMarker = [NSString stringWithFormat:@"%d-marker", zone.id];

        MKCircle *circle = [overlayCache objectForKey:keyMarker];
        if (circle == nil) {
            // draw the radius circle for the marker
            double radius = MAX(zone.markerRadius * 1.0, 1.0);
            circle = [MKCircle circleWithCenterCoordinate:zone.location radius:radius];
            [mapView addOverlay:circle];
            // store the circle in a cache for future reference
            [overlayCache setObject:circle forKey:keyMarker];
        }
    }
}

Code that makes the overlay views

#pragma mark -
#pragma mark MKMapViewDelegate
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay{
     MKCircleView *circleView = [[[MKCircleView alloc] initWithCircle:overlay] autorelease];
    circleView.lineWidth = 1.0;
    circleView.strokeColor = [UIColor redColor];
    return circleView;
}

Code that releases the overlay cache

- (void)dealloc {
    [overlayCache release];
    [mapView release];
    [super dealloc];
}
like image 790
rule Avatar asked Jul 30 '10 09:07

rule


1 Answers

I am seeing the same thing happen. I am drawing MKPolylines instead of circles, but I have exactly the same problem. 1 line works fine, but when I start to add several and try moving the map around it crashes with memory warnings. I'd paste my code, but it is pretty much identical to the above changing circle for line.

EDIT: the problem seems to be that each overlay creates a new core animation layer. there is a workaround here - https://devforums.apple.com/thread/48154?tstart=0 Also, I believe this is a known bug that should be fixed in the next release

EDIT:the workaround - "It isn't a problem with the API but rather the implementation. My suggestion to manually consolidate them into one is a workaround for the moment.

For example, here's how you could implement a MultiPolygon and corresponding view:"

    @interface MultiPolygon : NSObject <MKOverlay> {
    NSArray *_polygons;
    MKMapRect _boundingMapRect;
}

- (id)initWithPolygons:(NSArray *)polygons;
@property (nonatomic, readonly) NSArray *polygons;

@end

@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;
}

- (void)dealloc
{
    [_polygons release];
    [super dealloc];
}

- (MKMapRect)boundingMapRect
{
    return _boundingMapRect;
}

- (CLLocationCoordinate2D)coordinate
{
    return MKCoordinateForMapPoint(MKMapPointMake(MKMapRectGetMidX(_boundingMapRect), MKMapRectGetMidY(_boundingMapRect)));
}

@end



@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
like image 169
clarky Avatar answered Oct 12 '22 18:10

clarky