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?
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
}
});
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~
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With