Search code examples
objective-cmultithreadingmacoscocoansprogressindicator

Force update UI on very demanding app


Here is my problem : I have an app that has to do literally millions of calculs (This is for a scientific paper to be published).

So in order to speed up calculs I learned a little about threads and did this :

dispatch_queue_t myFirstQueue =
    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, myFirstQueue, ^{
    [Magic defineListOfSameDonInParticule:sharedData.particle];

    NSMutableDictionary *results = [Magic
            meanResultForSameDonInParticule:sharedData.particle];
});

And in Magic.m I have a method named defineListOfSameDonInparticule: in which there is this:

[particule.donorsList enumerateKeysAndObjectsWithOptions:NSEnumerationConcurrent
                     usingBlock: ^(id keyA, Molecule *alpha, BOOL *stop){

    [particule.donorsList enumerateKeysAndObjectsWithOptions:NSEnumerationConcurrent
                   usingBlock: ^(id keyB, Molecule *beta, BOOL *stop2){

        if ([alpha isNotEqualTo:beta]) {
            float distance = [self distanceBetweenMolecule:alpha
                                               andMolecule:beta];

            [alpha.closestSame setObject:[NSNumber numberWithFloat:distance]
                                  forKey:beta.molID];

            [appDelegate increaseProgressionMeterByOne];
        }

    }];

in this piece of code I have [appDelegate increaseProgressionMeterByOne] which sends a message to my app delegate which in turn sends incrementBy:1 to my progress bar

BUT all my threads are used by the millions of calculs happening at the same moment and so the UI freezes and my progressBar doesn't go forward.

To be noted : my progress bar works fine in other parts of the program which doesn't use multi-threading

Is there a way to force update the UI ? Or do you have any idea on how to solve this ?

I know updating the UI will slow down the app but it's necessary to the people the app is intended to !

Thanks a lot !

UPDATE :

Here is the code updating the UI in the AppDelegate

- (void)increaseProgressionMeterByOne {
    [progressBar incrementBy:1];
}

Solution

  • Andrew's answer shows how to wrap the call to the method to perform it on the main thread.

    dispatch_async(dispatch_get_main_queue(), ^
    {
       [appDelegate increaseProgressionMeterByOne];
    });
    

    Alternatively, if you want to easily be able to call this method from different threads safely, you could perform the GCD call directly in the method itself by doing something like this:

    - (void)increaseProgressionMeterByOne 
    {
        if([NSThread isMainThread])
        {
            [progressBar incrementBy:1];
        }
        else
        {
            dispatch_async(dispatch_get_main_queue(), ^
                {
                    [progressBar incrementBy:1];
                });
        } 
    }
    

    If your method were to look something like that, you could safely call it from any thread and it would automatically either stay on the main thread, or use GCD to get on it.