1. 程式人生 > >Effective Objective-C(第41-46條)gcd大中樞派發、performSelector

Effective Objective-C(第41-46條)gcd大中樞派發、performSelector

    OC中多執行緒程式設計的核心就是block與gcd。這雖然是兩種不同的技術,但他們是一併引入的。block是一種可在C、C++及OC程式碼中使用的“詞法閉包”(lexical closure),它極為有用,藉此機制,開發者可將程式碼像物件一樣傳遞,令其在不同環境(context)下執行。在block的範圍內,它可以訪問到其中的全部變數。

    gcd是一種與block有關的技術,它提供了對執行緒的抽象,而這種抽象基於“派發佇列”(dispatch queue)。開發者可將block排入佇列中,有gcd負責處理所有排程事宜。gcd會根據系統資源情況,適時得建立、複用、摧毀後臺執行緒,以便處理每個佇列。此外,使用GCD還可以方便的完成常見程式設計任務,比如編寫“只執行一次的執行緒安全程式碼”(thread-safe single-code execution),或者根據可用的系統資源來併發執行多個操作。

    block和gcd是當前OC的程式設計基石。因此必須理解其工作原理及功能

第41條:多用派發佇列,少用同步鎖

   在OC中,如果有多個執行緒要執行同一份程式碼,那麼有時候可能會出問題。一般採用鎖來實現某種同步機制。在GCD出現之前,第一種是採用“同步Block”,另一個辦法是使用NSLock物件。這兩種都會遇到死鎖現象。使用gcd,它能以簡單、高效的形式為程式碼枷鎖。

    “串行同步佇列”(serial synchronization queue)。將讀取/寫入都安排在同一個佇列裡,即可保證資料同步。

_syncQueue = dispatch_queue_create("com.effective-c.syncQueue",NULL);
-(NSString*) someString {
    __block NSString* localSomething;
    dispatch_sync(_syncQueue,^{
        localSomeString = _someString;
    });
    return localSomeString
}
-(void) setSomeString:(NSString*) someString{
    dispatch_sync(_syncQueue,^{
        _someString = someString;
    });
}

    此模式的思路是:把設定操作與獲取操作都安排在序列化的佇列裡執行,這樣的話,所有的針對屬性的訪問都是同步了。這點很想symbian的多動物件(OA)

    讀、寫操作。前者可以併發讀取,但是後者不能併發。可以優化程式碼如下:

_syncQueue = dispatch_get_global_create("DISPATCH_QUEUE_PRIORITY_DEFAULT",NULL);
-(NSString*) someString {
    __block NSString* localSomething;
    dispatch_sync(_syncQueue,^{
        localSomeString = _someString;
    });
    return localSomeString
}
-(void) setSomeString:(NSString*) someString{
    dispatch_barrier_async(_syncQueue,^{
        _someString = someString;
    });
}
    其中dispatch_barrier_async是柵欄,在佇列中,柵欄塊必須單獨執行,不能與其他塊並行。如圖所示:


第42條:多用gcd,少用performSelector系列方法

【本節要點】

● performSelector系列方法存在記憶體管理方面容易有疏忽。它無法確定將要執行的選擇子具體是什麼,因而ARC編譯器也就無法插入適當的記憶體管理方法。

● performSelector系列方法能處理的選擇子太過侷限了,選擇子的返回值型別及傳送給方法的引數個數都有限制。

● 如果想把任務放到另一個執行緒上執行,那麼最好不要用performSelector系列方法,而是使用gcd實現。

第43條:掌握gcd及操作佇列的使用時機

    很少有其他技術能與gcd的同步機制相媲美。對於那些只需要執行一次的程式碼來說,也是如此,使用gcd的dispatch_once最為方便。然而在執行後臺任務時候,gcd並不一定是最佳方式。還有一種技術叫做NSOpertationQueue,它雖然與gcd不同,但是卻與之相關,開發者可以把操作以NSOpertation子類的形式放到佇列中,這些操作也能夠併發執行。

    在兩者差別中,首先要注意:gcd是純c的API,而操作佇列是OC物件。gcd處理輕量級block而操作佇列處理重量級OC物件。

    用NSOperationQueue類的addOperatonWithBlock方法搭配NSBlockOperation類來操作佇列,其語法與純gcd方法非常類似。使用NSOperation和NSOpertaionQueue的好處如下:

● 取消某個操作

● 指定操作間的依賴關係

● 通過鍵值觀測機制監控NSOperation屬性。

● 指定操作的優先順序

● 重用NSOperation物件。

操作佇列很多地方勝過派發佇列。操作佇列提供了很多執行任務的方法,而且都是寫好了,直接就能用。開發者不用再編寫複雜的排程器。

NSNotificationCenter使用了操作佇列

本節要點

● 在解決多執行緒與任務管理問題時,派發佇列並非唯一方案。

● 操作佇列提供了一套高層次的OC API。能實現純gcd所具備的絕大部分功能,而且還能完成一些更為複雜的操作,那些操作如果該用gcd來實現,則需要另外寫程式碼。

第44條:通過dispatch group機制,根據系統資源狀況來執行任務

(帶補充)

第45條:使用dispatch_once來執行只需執行一次的執行緒安全程式碼

直接看程式碼吧,通常是單例的實現。

+(id) sharedInstance{
    static EOCClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{sharedInstance = [[self alloc]init];});
return sharedInstance;
}

第46條:不要使用dispatch_get_current_queue

-(NSString*) someString {
    __block NSString* localSomething;
    dispatch_sync(_syncQueue,^{
        localSomeString = _someString;
    });
    return localSomeString
}
-(void) setSomeString:(NSString*) someString{
    dispatch_sync(_syncQueue,^{
        _someString = someString;
    });
}