3

I've looked at other reports on this, and none seems to apply. Our app is being crippled by frequent (but not reliably reproducible) crashes like this:

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libobjc.A.dylib                 0x333a6c98 objc_msgSend + 16
1   MapKit                          0x30be52fc -[MKMapView annotationContainer:viewForAnnotation:] + 36
2   MapKit                          0x30be4f8e -[MKAnnotationContainerView _addViewForAnnotation:] + 270
3   MapKit                          0x30c0f164 -[MKAnnotationContainerView addViewForManagedAnnotation:notifyDelegate:] + 12
4   MapKit                          0x30c0b874 -[MKMapView(UserPositioningInternal) _runPositioningChange] + 1036
5   MapKit                          0x30c09a86 -[MKMapView(UserPositioningInternal) _startPositioningChange:] + 22
6   MapKit                          0x30c0d04a -[MKMapView(UserPositioningInternal) locationManagerUpdatedLocation:] + 578
7   CoreFoundation                  0x360bcefc -[NSObject(NSObject) performSelector:withObject:] + 16
8   CoreFoundation                  0x360fa2f2 -[NSArray makeObjectsPerformSelector:withObject:] + 394
9   MapKit                          0x30bfc802 -[MKLocationManager _reportLocationStatus:] + 34
10  MapKit                          0x30bfdd6c -[MKLocationManager _reportLocationSuccess] + 36
11  MapKit                          0x30bfd9c6 -[MKLocationManager locationManager:didUpdateToLocation:fromLocation:] + 674

Reports of this turn up a lot on the Web, but many seem to go unsolved. I'm not doing anything crazy, just showing the user position and one marker on the map. I followed the examples I could find, and I've looked at the code for goofs but can't find any.

Here's how my MapView delegate handles viewForAnnotation:

- (MKAnnotationView*)mapView:(MKMapView*)theMapView viewForAnnotation:(id <MKAnnotation>)annotation
{
    // If it's the user location, just return nil.
    if([annotation isKindOfClass:[MKUserLocation class]])
    {
        return nil;     // Use default system user-location view.
    }
    else
    {
        // Try to dequeue an existing pin view first.
        MKPinAnnotationView* pinView = (MKPinAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:@"stashMarkerID"];
        if(!pinView)
        {
            // If an existing pin view was not available, create one.
            pinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation
                                                       reuseIdentifier:@"stashMarkerID"] autorelease];
            pinView.pinColor = MKPinAnnotationColorPurple;
            pinView.animatesDrop = YES;
            pinView.canShowCallout = NO;
        }
        else
        {
            pinView.annotation = annotation;
        }

        return pinView;
    }
}

The crash appears to be associated with a change in the user's location, but once the user-location marker is added, I don't mess with it. When I need to refresh the one other map marker, I skip the user marker when removing the other one:

// Add the marker to the map.
// Remove old one(s) first.
int i = 0;
while(i < [mapView.annotations count])
{
    if ([[mapView.annotations objectAtIndex:i] isKindOfClass:[StashMarker class]])
    {
        [mapView removeAnnotation:[mapView.annotations objectAtIndex:i]];
    }
    else
    {
        i++;
    }
}

The delegate is the controller for the whole screen, so it's not being deallocated; the crash occurs while the screen is up. Not that it matters, but the map display looks like this:

enter image description here

Any guesses or insight would be much appreciated! This is on iOS 5.0.1.

UPDATE: We've found that this crash is occurring when no MKMapView should even be in existence. The view containing it has long since been popped. I wonder if we're experiencing the problem reported here: MKMapView crashes app when view controller popped

UPDATE 2: Here's another report of essentially the same thing: Why am I crashing after MKMapView is freed if I'm no longer using it?

This seems unusually messy for Cocoa, having to set the MKMapView's delegate to nil after we'd expect the MapView to be deallocated. At that rate, why don't we have to do this for all controls that take a delegate?

Community
  • 1
  • 1
Oscar
  • 2,039
  • 2
  • 29
  • 39
  • In future questions, please don't post screenshots of code and logs. Instead, copy and paste the text. –  Nov 16 '11 at 03:23
  • Are you using CLLocationManager? In what method is the code that removes annotations (which I would do differently)? What is done before or after that code? What code do you have in the didUpdateUserLocation or didUpdateToLocation delegate methods? –  Nov 16 '11 at 03:27
  • How would you remove annotations differently? – Oscar Nov 16 '11 at 07:50
  • First paste the text directly into the question preferably with no indentation. Then select the block of code or log and press the `{}` button in the editor toolbar. The reason it didn't syntax highlight the code properly is because the question didn't have the `ios` tag. The tags determine the syntax highlighting though this can be manually overridden. –  Nov 16 '11 at 11:16
  • Can you post the complete refreshContents method? I would remove the annotations by adding the ones to remove to another array and then call removeAnnotations (plural) instead of removing in-place and using the specific index to refer to an annotation. –  Nov 16 '11 at 11:20
  • The `objective-c` or `iphone` tags would also have worked. –  Nov 16 '11 at 11:25
  • Thanks for the info. I wish they'd present that information somehow; it's not even in the formatting-help info. There's no way I would've guessed it had anything to do with the tags on the question itself. – Oscar Nov 17 '11 at 02:19
  • Any particular reason you'd prefer to collect the annotations to remove and do them all at once? I'm wondering if MapKit's not thread-safe in this area. I am manipulating the annotations on the main thread, though. – Oscar Nov 17 '11 at 02:20
  • possible duplicate of [MKMapView crashes app when view controller popped](http://stackoverflow.com/questions/7269303/mkmapview-crashes-app-when-view-controller-popped) – Flexo Nov 20 '11 at 21:57

3 Answers3

10

OK, this is the real answer. It's from the Apple doc, but it's missing from MKMapView. It's only found under the documentation for its delegate protocol:

"Before releasing an MKMapView object for which you have set a delegate, remember to set that object’s delegate property to nil. One place you can do this is in the dealloc method where you dispose of the map view."

NOTE: This also applies to UIWebView.

I set the MapView's delegate pointer to nil in the delegate's dealloc method, and our crashes seem to have been eliminated.

Oscar
  • 2,039
  • 2
  • 29
  • 39
  • Hum, cool, I'll test that. Doesn't look to hurt to try it. Even looks like the right way of doing things anyway :) – Thibault D. Aug 19 '13 at 08:39
  • 1
    Hum, okey. My application used to randomly crash within 30seconds (or so) with a similar stack trace when scrolling around on MapView with location updates enabled. Now I've set the delegate to nil on view controller dealloc's I've been able to scroll around during 5 minutes without any problem. Looks good, thanks. – Thibault D. Aug 19 '13 at 09:07
2

I had the similar problem, and I ve tried your solution to set my mapView.delegate=nil but it works only for the 1st time when I redraw a mapview. after the second time it crashes. In my case error happens at this line. [CLPlacemark dealloc]

And my 'StashMarker' is derived from MKPlacemark. So I just change my constructor of 'StashMarker' From this

self = [super init];

To this

self = [super initWithCoordinate:your_coord addressDictionary:[NSDictionary dictionary]];

CLPlacemark which is the parent of MKPlacemark and your Annotation class needs an 'addressDictionary' to be released.

Soyoes
  • 940
  • 11
  • 16
0

(Disclaimer: I don't know if this is causing your issue.)

In the comments, I said:

I would remove the annotations by adding the ones to remove to another array and then call removeAnnotations (plural) instead of removing in-place and using the specific index to refer to an annotation.

and you asked:

Any particular reason you'd prefer to collect the annotations to remove and do them all at once?

Actually, it's not the removing-all-at-once part that's important but the reliance on the array index.

I'd prefer not to assume that the annotation I get at index i on one line will still be there at another time (even the next line) even if my code doesn't add or remove any annotations in between.

The map view (I think) just supplies the annotation list as an array to us for convenience but internally it may be storing the list differently and may shuffle the contents around (but not change the annotation references).

I feel it's safer not to assume that the annotations array contents will remain in fixed positions.

So to remove only certain annotations, I'd prefer this:

NSMutableArray *annotationsToRemove = [NSMutableArray array];

for (id<MKAnnotation> ann in mapView.annotations) 
{
    if ([ann isKindOfClass:[StashMarker class]])
    {
        [annotationsToRemove addObject:ann];
    }
}

[mapView removeAnnotations:annotationsToRemove];

Again, this may have nothing to do with the crashes you are experiencing.

  • Thanks Anna. I did try switching to the method you outline above, but this thing is still crashing. I wonder if the MapView might be calling viewForAnnotation for an annotation that has been removed. I guess the only way to find out is to put a lock in the part of my code that revises the annotations and in the delegate's viewForAnnotation method; in viewForAnnotation I can search the MapView's annotations for the one that I'm supposed to be providing a view for, to ensure that it's still in there. I don't know what else to do at this point. This is a pretty big bug. – Oscar Nov 17 '11 at 07:58
  • Have you tried disabling showsUserLocation to see if it really is related to that? Try starting a new project and add one function at a time until the crash is reproduced. Or try the reverse by commenting out one function at a time from the current code until the crash stops. –  Nov 17 '11 at 11:27
  • I've updated the question with findings from elsewhere. It looks like MKMapViews hang around after you'd expect them to be deallocated, calling delegates that have long since been destroyed. – Oscar Nov 19 '11 at 20:13