mapbox / Simple-KML

Simple KML is a simple & lightweight parsing library for KML written in Objective-C for the iOS platform.
BSD 3-Clause "New" or "Revised" License
208 stars 58 forks source link

Getting exception as “Collection was mutated while being enumerated” #11

Closed kohdesmond closed 12 years ago

kohdesmond commented 12 years ago

Hi Guys,

I tried to check out a user pinpoint with polygon by looping through all the KMLs. the app always crash @ this point:

//create KML in hidden Mapview -(void)loadKML:(NSMutableArray *)kmlNameArray {
//dispatch_group_t group = dispatch_group_create();

//remove polygon and redraw again.
[NSThread detachNewThreadSelector: @selector(spinEnd) toTarget:self withObject:nil];

[mapView removeOverlays:mapView.overlays];
[inUserRangeArray removeAllObjects];
[inUserRangeArrayObjectIndex removeAllObjects];
[scrollview removeFromSuperview];
[pageControl removeFromSuperview];
[NSThread detachNewThreadSelector: @selector(spinBegin) toTarget:self withObject:nil];

NSArray *sysPaths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES );
NSString *docDirectory = [sysPaths objectAtIndex:0];   

for (int e=0; e<[kmlNameArray count]; e++) 
{        
    //NSString *kmlNameStr = [kmlNameArray objectAtIndex:e];
    Frog *kmlID = [self.fs objectAtIndex:e];
    self.kmlID = [NSString stringWithFormat:@"%i",kmlID.fID];
    self.kmlIDObjectIndex = [NSString stringWithFormat:@"%i",e];

    NSLog(@"asasas %@",kmlIDObjectIndex);

    //NSLog(@"KML items %@", kmlNameStr);          
    //NSLog(@"KML ID %@", kmlID);
    //NSLog(@"KML file Path %@",[NSString stringWithFormat:@"%@/data/%@/%@", docDirectory,self.kmlID,[kmlNameArray objectAtIndex:e]]);

    SimpleKML *kml = [SimpleKML KMLWithContentsOfFile:[NSString stringWithFormat:@"%@/data/%@/%@", docDirectory,self.kmlID,[kmlNameArray objectAtIndex:e]]error:NULL];

    // look for a document feature in it per the KML spec

    //        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{

    if (kml.feature && [kml.feature isKindOfClass:[SimpleKMLDocument class]])
    {// see if the document has features of its own
        for (SimpleKMLFeature *feature in ((SimpleKMLContainer *)kml.feature).features)
        {// otherwise, see if we have any placemark features with a polygon
            if ([feature isKindOfClass:[SimpleKMLPlacemark class]] && ((SimpleKMLPlacemark *)feature).polygon)
            {
                SimpleKMLPolygon *polygon = (SimpleKMLPolygon *)((SimpleKMLPlacemark *)feature).polygon;
                SimpleKMLLinearRing *outerRing = polygon.outerBoundary;
                //points[i], i = number of coordinates
                CLLocationCoordinate2D points[[outerRing.coordinates count]];
                NSUInteger i = 0;
                for (CLLocation *coordinate in outerRing.coordinates)
                { 
                    points[i++] = coordinate.coordinate;
                }
                // create a polygon annotation for it
                self.overlayPolygon = [MKPolygon polygonWithCoordinates:points count:[outerRing.coordinates count]];

                //crash here
                [mapView addOverlay:overlayPolygon];

                // zoom the map to the polygon bounds
                [mapView setVisibleMapRect:overlayPolygon.boundingMapRect animated:YES];

            }
        }
    } 
}
incanus commented 12 years ago

It's not really clear from your code where this error is happening. Does it give you any info as to which collection is being enumerated/mutated? Simple KML should return immutable objects. Are you maybe altering kmlNameArray on another thread or GCD queue?

kohdesmond commented 12 years ago

Hi Incanus,

Thanks for the reply,

i call loadKML:(NSMutableArray *)kmlNameArray from initKMLArray

//location found start reading KML from bundle(at the moment from bundle) -(void)initKMLArray {
NSMutableArray tempKMLArray; tempKMLArray = [[NSMutableArray alloc] init]; //testing first 9 kml only should be [self.frogs count] NSString kmlName; // NSString kmlID;
for (int i=0; i<[self.frogs count]; i++) {
Frog
kmlFrog = [self.frogs objectAtIndex:i]; kmlName = kmlFrog.distributionKML; ////nslog(@"KML %@",kmlFrog.distributionKML); //kmlID = [NSString stringWithFormat:@"%d",kmlFrog.frogID]; [tempKMLArray addObject:kmlName]; } //[kmlName release];

////nslog(@"tempKMLArray items %@", tempKMLArray);

dispatch_async( dispatch_get_global_queue(0, 0), ^{

    [self loadKML:tempKMLArray];
});

}

[self initKMLArray] will be called when location didUpdateToLocation, usually by user pressing refresh button. i did a BOOL, if searching the refresh button are disabled.

incanus commented 12 years ago

I think your problem is the use of dispatch_get_global_queue(). This is a global concurrent queue, not the main queue which runs on the main UI thread. I think you should try dispatch_get_main_queue() instead. This isn't related to Simple KML but instead to the proper use of Grand Central Dispatch.