2

I am adding about 3000 MKOverlays to my map, and as you can imagine, it takes a while, up to about eight seconds sometimes. I'm looking for a way to use threading to improve performance, so the user can move the map around while overlays are being added. Preferably, the overlays would be added sequentially, starting with the just the ones within the map's region. I have tried something along these lines with GCD:

- (MKOverlayView*)mapView:(MKMapView*)mapView viewForOverlay:(id)overlay {
    __block MKPolylineView* polyLineView;

    //do the heavy lifting (I presume this is the heavy lifting part, but 
    // because this code doesn't compile, I can't actually *test* it)
    // on a background thread
    dispatch_async(backgroundQueue, ^ { 
        polyLineView = [[[MKPolylineView alloc] initWithPolyline:overlay] autorelease];
        [polyLineView setLineWidth:11.0];

        //if the title is "1", I want a blue line, otherwise red
        if([((LocationAnnotation*)overlay).title intValue]) {
            [polyLineView setStrokeColor:[UIColor blueColor]];
        } else {
            [polyLineView setStrokeColor:[UIColor redColor]];
        }

        //return the overlay on the main thread
        dispatch_async(dispatch_get_main_queue(), ^(MKOverlayView* polyLineView){
            return polyLineView;
        });
    });
}

But because GCD blocks are defined with void parameter and return types, this code doesn't work- I get an incompatible pointer type error on the return line. Is there something I am missing here, or another way to thread this? Or perhaps an entirely different way to improve the performance of the overlay-adding process? I appreciate any and all help!

Edit:

I have found that the problem is not where I actually add the overlays here:

for(int idx = 1; idx < sizeOverlayLat; idx++) {
    CLLocationCoordinate2D coords[2];
    coords[0].latitude = [[overlayLat objectAtIndex:(idx - 1)] doubleValue];
    coords[0].longitude = [[overlayLong objectAtIndex:(idx - 1)] doubleValue];

    coords[1].latitude = [[overlayLat objectAtIndex:idx] doubleValue];
    coords[1].longitude = [[overlayLong objectAtIndex:idx] doubleValue];

    MKPolyline* line = [MKPolyline polylineWithCoordinates:coords count:2];
    [line setTitle:[overlayColors objectAtIndex:idx]];

    [mapViewGlobal addOverlay:line];
}

Adding all 3000 takes maybe 100ms here. The part that takes a long time (I assume) is where I actually create the overlays, in the first method I showed.

eric.mitchell
  • 8,817
  • 12
  • 54
  • 92

1 Answers1

0

There is a little gap between what you want and what the compiler can do. When you are calling dispatch_async, you are actually telling the CPU "here, have this chunk of code, and run it whenever you feel like it, not now, not blocking my user interface thread". But, your method has to return now. There is simply no way for you to create anything in a background thread, because you are going to have to wait for it anyway before mapView:viewForOverlay: returns, since it has to return something.

This method is not the place to use GCD or any background code. If your problem is the addition of a big number of overlays at once, I would split all the overlays into chunks of say 100 and add them to the map with a delay of 100ms between each batch.

Grzegorz Adam Hankiewicz
  • 7,349
  • 1
  • 36
  • 78
  • Please see my edit. If threading here isn't possible, where can I apply it to improve performance, or what other tactics could I use to improve the performance here? I'm not really sure what else to try. – eric.mitchell Feb 22 '12 at 19:10
  • Add batches separated by a time interval. – Grzegorz Adam Hankiewicz Feb 23 '12 at 08:43
  • I tried that. I experimented with different batch/interval sizes, but short of adding just one overlay at a time with a .1 second interval or adding ten at a time with a 1 second interval, the UI still become laggy enough to the point where it was unusable. – eric.mitchell Feb 23 '12 at 12:38
  • According to your first claim adding 3000 overlays takes 8 seconds, so adding 50 overlays every second should drag you down 0.13 seconds. Try adding 50 overlays, then wait 0.13 seconds. It should interleave enough time for the UI and take you 16 seconds to add all 3000 overlays. If this is not good, you simply have to not overload the map with so many overlays. Try coalescing them into one. I would investigate changing color of each segment so you can *join* them with translucent segments and they look separate to the user. – Grzegorz Adam Hankiewicz Feb 23 '12 at 14:52
  • Your calculations are not correct; if I add 50 overlays each second, then that would take 60 seconds. I have decided to combine them all into one big segment, which takes almost no time at all. – eric.mitchell Feb 23 '12 at 18:47
  • @Rickay How did you combine them all into 'one big segment'? – Robert Wagstaff Jun 08 '13 at 08:12
  • Instead of 3000 individual overlays with 2 points each, I just created 1 large overlay with all 3000 points in it. – eric.mitchell Jun 09 '13 at 01:23