Search code examples
iosobjective-cgrand-central-dispatchmbprogresshud

How to illustrate a background task using GCD?


I want to illustrate the progress on MBProgressHUD item, but when i triger this method :

- (IBAction)signInBttn:(id)sender {

    MBProgressHUD *hudd = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
    hudd.mode = MBProgressHUDModeAnnularDeterminate;
    hudd.labelText = @"Loading";

    __block float value = 0;
    for (int j = 0; j<2000; j++) {
        dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

            for (int i = 0; i<20000 ; i++) {

            }
            value += 0.001;
            dispatch_async( dispatch_get_main_queue(), ^{
                hudd.progress = value;
            });

        });
    }        
}

hud appears fully to 100%. This is only for my information, I dont have idea how to create background task which calculate something and when he done with e.g. 40% the HUD is refreshing to 40% of his progress. I hope I made ​​myself clear, and if anyone has time to help improve my code, thanks a lot for any answers


Solution

  • In this case, you can solve the problem by decoupling the updating of the counter from the updating of your HUD in your UI. Apple refers to this as "updating the state asynchronously" in WWDC 2012 video Asynchronous Design Patterns with Blocks, GCD, and XPC.

    Generally this isn't necessary (most of the time the stuff we're doing asynchronously is slow enough that we don't have problems), but if doing something that is running faster than the UI can hope to keep up with, you create a "dispatch source" for this. I'm going to illustrate it with a UIProgressView, but the same applies to pretty much any UI:

    // create source for which we'll be incrementing a counter,
    // and tell it to run the event handler in the main loop
    // (because we're going to be updating the UI)
    
    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
    
    // specify what you want the even handler to do (i.e. update the HUD or progress bar)
    
    dispatch_source_set_event_handler(source, ^{
        self.iterations += dispatch_source_get_data(source);
        [self.progressView setProgress: (float) self.iterations / kMaxIterations];
    });
    
    // start the dispatch source
    
    dispatch_resume(source);
    
    // now, initiate the process that will update the source
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    
        for (long i = 0; i < kMaxIterations; i++)
        {
            // presumably, do something meaningful here
    
            // now increment counter (and the event handler will take care of the UI)
    
            dispatch_source_merge_data(source, 1);
        }
    
        // when all done, cancel the dispatch source
    
        dispatch_source_cancel(source);
    });
    

    In my example, iterations is just a long property:

    @property (nonatomic) long iterations;
    

    And I defined my kMaxIterations constant as follows:

    static long const kMaxIterations = 10000000l;