jdg / MBProgressHUD

MBProgressHUD + Customizations
http://www.bukovinski.com/
MIT License
16.01k stars 3.56k forks source link

Message sent to deallocated instance when repeatedly performing operations that will show the HUD #275

Closed oseparovic closed 9 years ago

oseparovic commented 9 years ago

There's an issue in our app where if the user repeatedly performs a post status operation it will trigger the following crash.

*** -[UIViewAnimationState configureAnimation:forLayer:forKey:]: message sent to deallocated instance 0x174bdae50

The specific flow for this is:

  1. Click post status
  2. Composition modal dismisses
  3. HUD shown attached to window
  4. Status upload succeeds or fails
  5. HUD hidden with 2 second delay

If this sequence is repeated rapidly after a few operations this crash can be reproduced.

This is what our hideHUDAfterDelay helper method looks like:

- (void)hideHUDAfterDelay:(float)delay withMessage:(NSString*)message andCompletionType:(HUDCompletionType)type {
    // if a message has been supplied set it
    if (![NSString isEmpty:message]) {
        HUD.labelText = message;
    }

    // show the spinner for the time minus half a second then switch to checkmark and hide
    if (delay > switchToCheckDelay) {
        switch (type) {
            case kHUDCompletionX: {
                DLog(@"change hud to X delay:%f", delay-switchToCheckDelay);
                dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delay-switchToCheckDelay * NSEC_PER_SEC);
                dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
                    [self changeHUDLabelTo:@"×" andHide:YES];
                });
                break;
            }
            case kHUDCompletionCheck: {
                DLog(@"change hud to check delay:%f", delay-switchToCheckDelay);
                dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delay-switchToCheckDelay * NSEC_PER_SEC);
                dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
                    [self changeHUDLabelTo:@"✓" andHide:YES];
                });
                break;
            }
            case kHUDCompletionNone: {
                [self performSelector:@selector(hideHUD) withObject:nil afterDelay:delay];
                break;
            }
            default:
                break;
        }
    } else {
        // delay too short to switch to checkmark
        [self performSelector:@selector(hideHUD) withObject:nil afterDelay:delay];
    }
}

I've pasted a portion of the BT below.

(lldb) bt
* thread #20: tid = 0x211a6, 0x0000000181ec63d4 CoreFoundation`___forwarding___ + 968, queue = 'com.airg.imagequeue', stop reason = EXC_BREAKPOINT (code=1, subcode=0x181ec63d4)
    frame #0: 0x0000000181ec63d4 CoreFoundation`___forwarding___ + 968
    frame #1: 0x0000000181dcac4c CoreFoundation`_CF_forwarding_prep_0 + 92
    frame #2: 0x000000018698f7c8 UIKit`-[UIViewAnimationState animationForLayer:forKey:forView:] + 712
    frame #3: 0x0000000186705404 UIKit`-[UIViewAnimationState actionForLayer:forKey:forView:] + 44
    frame #4: 0x00000001866c9da0 UIKit`-[UIView(CALayerDelegate) actionForLayer:forKey:] + 140
    frame #5: 0x0000000186020c9c QuartzCore`-[CALayer actionForKey:] + 104
    frame #6: 0x0000000186016678 QuartzCore`actionForKey(CALayer*, CA::Transaction*, NSString*) + 100
    frame #7: 0x00000001860164e4 QuartzCore`CA::Layer::begin_change(CA::Transaction*, unsigned int, objc_object*&) + 176
    frame #8: 0x00000001860187d0 QuartzCore`CA::Layer::set_position(CA::Vec2<double> const&, bool) + 240
    frame #9: 0x00000001860186d4 QuartzCore`-[CALayer setPosition:] + 48
    frame #10: 0x0000000186018664 QuartzCore`-[CALayer setFrame:] + 456
    frame #11: 0x00000001866c9a3c UIKit`-[UIView(Geometry) setFrame:] + 292
    frame #12: 0x00000001866ddc14 UIKit`-[UIImageView _setViewGeometry:forMetric:] + 188
    frame #13: 0x0000000186728158 UIKit`-[UIActivityIndicatorView _feedTheGear] + 1276
  * frame #14: 0x000000018672a178 UIKit`-[UIActivityIndicatorView initWithActivityIndicatorStyle:] + 184
    frame #15: 0x0000000100228f24 <Redacted>`-[MBProgressHUD updateIndicators](self=0x00000001564f9c20, _cmd=0x00000001003bfeca) + 428 at MBProgressHUD.m:482
    frame #16: 0x00000001002269b0 <Redacted>`-[MBProgressHUD initWithFrame:](self=0x00000001564f9c20, _cmd=0x0000000186ea5004, frame=CGRect at 0x0000000104dbba60) + 1640 at MBProgressHUD.m:204
    frame #17: 0x0000000100226b54 <Redacted>`-[MBProgressHUD initWithView:](self=0x0000000000000000, _cmd=0x0000000186ee0dcc, view=0x0000000154d10f20) + 320 at MBProgressHUD.m:213
    frame #18: 0x00000001002259e0 <Redacted>`+[MBProgressHUD showHUDAddedTo:animated:](self=0x00000001004a0270, _cmd=0x00000001003aae92, view=0x0000000154d10f20, animated=true) + 104 at MBProgressHUD.m:116

Any ideas? Thanks in advance.

matej commented 9 years ago

Looks like you're doing UI operations on a background thread (thread #20, com.airg.imagequeue). You need to make sure you're always dealing with UIKit (and MBProgressHUD) on the main thread / queue.

oseparovic commented 9 years ago

You are 100% correct, my bad. I didn't even see that. Thank you so much for the quick response!