Search code examples
iphoneipadblock

iOS - blocks slow as hell


I have about 10 objects on an app's interface. These objects are basically a button that has a glow showing which button is active as a given time. When a button is selected its glow is turned on and all other buttons' glow are turned off.

To turn the glows on and off, I had this

[buttons enumerateObjectsWithOptions:NSEnumerationConcurrent
                                     usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    if (obj == sender)
            [(myButtonClass *)obj showGlow];
    else {
            [(myButtonClass *)obj hideGlow];
    }
}];

but I was forced to change to this

[buttons enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {

    if (obj == sender)
            [(myButtonClass *)obj showGlow];
    else {
            [(myButtonClass *)obj hideGlow];
    }
}];

removing the concurrent part of the enumeration. The problem was that for some buttons in particular, the glow was taking 5 seconds to change on or off, but just for the same buttons all the time. The impression I had is that they were in a kind of queue being processed in order (?????!!!)

The showGlow/hideGlow methods are basically two lines: setting a variable and setting the hidden property of the glow imageView on and off. So, these are fast methods for all buttons.

What I don't understand is why the problem happened for some buttons but not for others and why a block takes so long to process when the app is basically idle.

any clues? thanks.


Solution

  • One reason for the "slow" UI update could be that, when using the NSEnumerationConcurrent option, the enumeration happens on a thread other that the main thread. Since all UI updates should happen on the main thread, your UI changes wont get reflected right away, but instead after a delay when the run loop flushes all the changes made.

    In your case you have only 10 objects, which doesn't justify using the concurrent option. Besides, why complicate the code with blocks when you can just use the easy readable, and or that matter the fast enumeration, forin:

    for (UIButton *button in buttons) {
        if (button == sender) [button showGlow];
        else [button hideGlow];
    }
    

    You can experiment and observe the same delayed UI update if you start an activity indicator somewhere, then stop it in a background thread. It will be a couple of seconds till it actually stops.

    If you insist on using blocks, you have to call all UIKit methods in the main thread:

    [buttons enumerateObjectsWithOptions:NSEnumerationConcurrent
                                     usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        if (obj == sender)
            // One possibility with GCD and dispatch_async on the main queue
            dispatch_async(dispatch_queue_get_main(), ^{[(myButtonClass *)obj showGlow];});
        else {
            // another possibility to call the selector on the main thread
            [(myButtonClass *)obj performSelectorOnMainThread:@selector(hideGlow)];
        }
    }];