MKDotBounceAnimation animationDidStop bug

MKMapViews seem buggy when created problematically.

Some tips:

  1. set the delegate to nil when the delegate dealloc’s, as per that advice to fix one of the crashbugs
  2. Either make the instance it static so you never release it, or schedule a delayed ‘release’ command to avoid theanimationDidStop crashbug.

What I do to avoid theanimationDidStop crash is instead of calling release directly, I call it after a delay of 4 seconds. This is easy with this statement:

 [mapView performSelector:@selector(release) withObject:nil afterDelay:4.0f];

Just gives the internal stuff a chance to clean itself up before the release is sent.

EDIT: if you read the comments below, there are some even better workarounds. Jayant offers this approach which is much cleaner:

[mapView.layer removeAllAnimations];

It seems this MKDotBounceAnimation problem only occurs with programmatically created MKMapViews (not XIB ones), as my XIB one never seems to crash with that error.

PS. I’m the author of the app GPS Log, please check it out :)

15 comments on “MKDotBounceAnimation animationDidStop bug

  1. Even though I am using a nib file to create a MKMapView object, I meet a bug above sometimes. The bug is usually show up to me when I try to pop a top controller (which has a MKMapView object) on the navigation controller. IPhone SDK always call [MKDotBounceAnimation animationDidStop:finished:] and seems to free a object that is already freed.

  2. I’ve hit this bug mainly when I am testing (popping a view on, then quickly off before the current location has settled down from it’s bounce.) I’ve had some success setting show user location to NO, removing all annotations, unsetting the delegate inside “viewWillDisappear” … but when I tried that today (on a map view inside a cell) I didn’t have access to that event, and doing these ‘tricks’ in the cell dealloc failed for me.

    You fix is somewhat distateful but totally working (given current testing), so BIG thank you!

    BTW: My assumption is that your delay allows the bounce animation end event to occur, and notify (access) a not-yet-released object, so the crash doesn’t occur. Hopefully Apple will fix this soon.

  3. It’s a nasty bug.

    My latest workaround is just to never release it. I create it as a singleton so it doesn’t leak like crazy and just keep reusing the one instance.

    Never release = never get the bug! :) and maybe less distasteful than a delayed release.

  4. The following code will fix the bug by preventing the call of animationDidStop if the animation did not complete …

    @protocol FixedAnimationDelegate
    – (void)fixedAnimationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag;

    void fixedAnimationDidStop(id self, SEL cmd, CAAnimation *theAnimation, BOOL finished)
    if (cmd != NULL && finished)
    [self fixedAnimationDidStop:theAnimation finished:finished];
    NSLog(@”prevented map annotation view animation from causing a crash”);
    @catch (NSException *e)
    NSLog(@”prevented map annotation view animation from causing a crash”);

    + (void)initWorkarounds
    Class MKDotBounceAnimationClass = NSClassFromString(@”MKDotBounceAnimation”);
    if (MKDotBounceAnimationClass != nil)
    class_addMethod(MKDotBounceAnimationClass, @selector(fixedAnimationDidStop:finished:), (IMP)fixedAnimationDidStop, “v@:@c”);
    Method orgMethod = class_getInstanceMethod(MKDotBounceAnimationClass, @selector(animationDidStop:finished:));
    Method fixedMethod = class_getInstanceMethod(MKDotBounceAnimationClass, @selector(fixedAnimationDidStop:finished:));
    method_exchangeImplementations(orgMethod, fixedMethod);

  5. Perfect article, allowed me to save same problem but a bit different way
    My case was following: I leave screen with mapView, screen deallocs and in short after this (screen already destroyed) timer routine attempts to do this animation on deleted screen
    setting mapview delegate to nil sure doesn’t help: timer routine is already scheduled

    Solution: delay destruction of screen object exact the way you suggested
    – (void)dealloc {

    if(false) [super dealloc];
    [super performSelector:@selector(dealloc) withObject:nil afterDelay:4.0f];

  6. Has anyone tried?

    – (void)viewWillDisappear:(BOOL)animated {
    [mapView.layer removeAllAnimations];

  7. Rather than a specific layer this should just pull all you annotations. Turning of showsUserLocation afterward might border on superstition but I do it anyway.

    [self.mapView removeAnnotations:self.mapView.annotations];
    self.mapView.showsUserLocation = NO;

  8. Nick actually disabling user location allows the user location annotation to be added when you repopulate the annotations. Thats good stuff!

  9. None of these solutions work, just like 99.9% of all answers people give on the internet. removeAllAnimations is not a method that exists for MKMapView, not sure how anyone could say that one is working. Performing the delayed release only gives the trying to release an already released resource.

  10. @TheoJohn:
    To call removeAllAnimations you need to add QuartzCore.framework to your project and import CALayer.h to gain access to it.

Leave a Reply

Your email address will not be published. Required fields are marked *