Reputation: 2395
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:
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?
Upvotes: 3
Views: 3232
Reputation: 940
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.
Upvotes: 2
Reputation: 2395
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.
Upvotes: 10
Reputation:
(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.
Upvotes: 0