Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Block_release deallocating UI objects on a background thread

One of the patterns presented at the WWDC 2010 "Blocks and Grand Central Dispatch" talk was to use nested dispatch_async calls to perform time consuming tasks on a background thread and then update the UI on the main thread once the task is complete

dispatch_async(backgroundQueue, ^{
    // do something time consuming in background
    NSArray *results = ComputeBigKnarlyThingThatWouldBlockForAWhile();

    // use results on the main thread
    dispatch_async(dispatch_get_main_queue(), ^{
        [myViewController UpdateUiWithResults:results];
    });
});

Since "myViewController" is being used inside the blocks, it automatically gets a 'retain' and will later get a 'release' when the blocks are cleaned up.

If the block's 'release' call is the final release call (for example, the user navigates away from the view while the background task is running) the myViewController dealloc method is called -- but it's called on the background thread!!

UIKit objects do not like to be de-allocated outside of the main thread. In my case, UIWebView throws an exception.

How can this WWDC presented pattern - specifically mentioned as the best new way to avoid UI lockup - be so flawed? Am I missing something?

like image 734
Ralph Marczynski Avatar asked Jun 15 '11 05:06

Ralph Marczynski


2 Answers

You can use the __block storage type qualifier for such a case. __block variables are not automatically retained by the block. So you need to retain the object by yourself:

__block UIViewController *viewController = [myViewController retain];
dispatch_async(backgroundQueue, ^{
    // Do long-running work here.
    dispatch_async(dispatch_get_main_queue(), ^{
        [viewController updateUIWithResults:results];
        [viewController release]; // Ensure it's released on main thread
    }
});

EDIT

With ARC, __block variable object is automatically retained by the block, but we can set nil value to the __block variable for releasing the retained object whenever we want.

__block UIViewController *viewController = myViewController;
dispatch_async(backgroundQueue, ^{
    // Do long-running work here.
    dispatch_async(dispatch_get_main_queue(), ^{
        [viewController updateUIWithResults:results];
        viewController = nil; // Ensure it's released on main thread
    }
});
like image 113
Kazuki Sakamoto Avatar answered Sep 19 '22 12:09

Kazuki Sakamoto


In a thread, I just use [viewController retain]; then at the end of the thread use [viewController release]. It works and I don't use GCD~

like image 38
winlin Avatar answered Sep 20 '22 12:09

winlin