實戰一: 使用串行隊列實現簡單的預加載
主要思路: 使用gcd在子線程中創建并初始化一個ViewController,在其加載完畢后,將其push入navigation controller中
簡要代碼如下:
@implementation DWClassA
{
dispatch_queue_t _serialQueue;
UINavigationController *_navController;
}
- (dispatch_queue_t)serialQueue
{
if (!_serialQueue) {
_serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);//創建串行隊列
}
return _serialQueue;
}
- (void)prepareViewController
{
dispatch_async([self serialQueue], ^{//把block中的任務放入串行隊列中執行,這是第一個任務
self.viewController = [[[DWViewController alloc] init] autorelease];
sleep(2);//假裝這個viewController創建起來很花時間。。其實view都還沒加載,根本不花時間。
NSLog(@"prepared");
});
}
- (void)goToViewController
{
dispatch_async([self serialQueue], ^{//第二個任務,推入viewController
NSLog(@"go");
dispatch_async(dispatch_get_main_queue(), ^{//涉及UI更新的操作,放入主線程中
[_navController pushViewController:self.viewController animated:YES];
});
});
}
- (void)dealloc
{
_serialQueue && dispatch_release(_serialQueue);
[_navController release];
[_viewController release];
[super dealloc];
}
@end
首先我們創建了一個串行隊列(使用DISPATCH_QUEUE_SERIAL作為參數),我們知道,將多個任務dispatch至一個串行隊列,可以達到線程同步的效果,并且可以避免編寫Lock相關的代碼。我們使用一個方法來封裝這個串行隊列的創建,使得后續操作都能取得這個串行隊列
緊接著我們在prepare函數中,將創建并初始化ViewController的任務dispatch至串行隊列中,一旦dispatch,任務將立即開始執行,這里我使用sleep函數來模擬一個需要較長時間來初始化的viewcontroller,并且sleep了夸張的兩秒鐘。
然后我們在goTo函數中,將push任務dispatch至前述的串行隊列中,當初始化完成后,會自動接著執行push任務,當然,如果dispatch這個push任務時,如果前述的初始化任務早已完成,那么push任務將被立即執行。值得注意的是,在Cocoa開發中,我們必須將涉及UI更新的操作(即使這個操作不會被立即顯示在屏幕中)放在主線程中執行,這是Apple在文檔中規定的。
實際上Apple規定UIKit的方法都必須放在主線程中調用,事實證明,確實很多不涉及UI操作的UIKit方法都不能在子線程中調用,甚至一些文件加載的函數。而其根本原因是UIKit的這些方法都在內部調用了一些可能產生競態的資源(很多方法會調用render context來進行中間轉換等操作),而一些UIKit方法被驗證確實能放在子線程中調用(雖然不推薦這么做,因為這么做不穩定,你不能確定某一天Apple會修改這些方法的內部實現而導致其只能在主線程中執行)也印證了這一點。
最后我們在dealloc函數中釋放應該釋放的資源,注意dispatch_release方法不可以傳入一個NULL指針,這將導致崩潰。
在GCD的頭文件中我們會發現這句注釋:The result of passing NULL in this parameter is undefined.也就是說,不像free函數那樣(free函數由POSIX定義傳入NULL則什么都不干),dispatch_release是不能接受空指針的。
實戰二:資源競爭
參考鏈接:http://mobile.51cto.com/hot-403011.htm
http://www.dreamingwish.com/article/gcd-practice-io-race.html