iOS 編寫高質量Objective-C代碼(七)

級別: ★★☆☆☆
標籤:「iOS」「GCD」「Objective-C」
做者: MrLiuQ
審校: QiShare團隊php

前言:
這幾篇文章是小編在鑽研《Effective Objective-C 2.0》的知識產出,其中包含做者和小編的觀點,以及小編整理的一些demo。但願能幫助你們以簡潔的文字快速領悟原做者的精華。
在這裏,QiShare團隊向原做者Matt Galloway表達誠摯的敬意。git

文章目錄以下:
iOS 編寫高質量Objective-C代碼(一)
iOS 編寫高質量Objective-C代碼(二)
iOS 編寫高質量Objective-C代碼(三)
iOS 編寫高質量Objective-C代碼(四)
iOS 編寫高質量Objective-C代碼(五)
iOS 編寫高質量Objective-C代碼(六)
iOS 編寫高質量Objective-C代碼(七)
iOS 編寫高質量Objective-C代碼(八)github


本篇的主題是iOS中的 「 大中樞開發 GCD 」面試

先簡單介紹一下今天的主角:GCD安全

  • GCD(Grand Central Dispatch):一種與塊相關的技術,提供了對線程的抽象管理(基於派發隊列dispatch queue)。GCD會根據系統資源狀況,適時且高效地 「建立線程」 、「複用線程」 、 「銷燬線程」

1、多用派發隊列,少用同步鎖

問:在iOS開發中,如何經過鎖來提供同步機制?(之前面試中,常常問道的問題..)bash

答:在GCD出現以前,有兩種方式:微信

  • 同步塊:@synchronized(self) {...}
- (void)synchronizedMethod {
    
    @synchronized (self) {
        
        // Safe area...
    }
}
複製代碼
  • NSLock:[_lock lock]; & [_lock unlock];
_lock = [[NSLock alloc] init];

- (void)synchronizedMethod {
    
    [_lock lock];    
    // Safe area..    
    [_lock unlock];
}
複製代碼

不過這兩種寫法效率很低,若是有不少屬性,那麼每一個屬性的同步塊都要等其餘同步塊執行完畢才能執行。併發

GCD出現後,GCD與Block相結合,使開發變得更加簡單、高效。async

問:如何保證屬性讀寫時線程絕對安全? 答:在屬性寫入時,使用柵欄塊barrier。只有當前全部併發塊都執行完畢後,纔會執行barrier塊,而後纔會繼續向下處理。函數

  • 思路以下:

  • 代碼以下:
_syncQueue = dispatch_queue_create("syncQueue", DISPATCH_QUEUE_CONCURRENT);

//! 讀取字符串
- (NSString *)someString {

    __block NSString *localSomeString;

    dispatch_sync(_syncQueue, ^{
        localSomeString = _someString;
    });

    return localSomeString;
}

- (void)setSomeString:(NSString *)someString {

     dispatch_barrier_async(_syncQueue, ^{
        _someString = someString;
    });
}
複製代碼

2、多用GCD,少用performSelector系列方法

performSelector系列方法的缺點有兩個:

  1. performSelector系列方法可能引發內存泄漏: 在ARC環境下,編譯器並不知道將要調用的選擇子是什麼,有沒有返回值,返回值是什麼,因此ARC不能判斷返回值是否能釋放,所以ARC作了一個比較謹慎的作法:只添加retain,不添加release。所以在有返回值或參數的時候可能致使內存泄漏。
  2. performSelector系列方法的返回值只能是void或OC對象類型。
  3. performSelector系列方法最多隻能傳入兩個參數。

所以可使用GCD來代替performSelector系列方法:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
    //do something..
});
複製代碼

3、掌握GCD及操做隊列的使用時機

GCD性能很棒,但在執行後臺任務時,GCD並不必定是最佳選擇。在iOS開發中,還有一種技術叫NSOperationQueueGCD是基於C語言的API,性能較高。而NSOperationQueue是基於GCD的抽象。

使用NSOperationNSOperationQueue的優勢:

  • 支持取消某個NSOperation: 在運行任務前,能夠在NSOperation對象上調用cancel方法,用以代表此任務不須要執行。不過已經啓動的任務沒法取消。iOS 8以前,GCD隊列是沒法取消的,GCD是「安排好以後就無論了(fire and forget)」。iOS 8以後,支持dispatch_canceldispatch_block_cancel

  • NSOperation支持多任務操做的依賴關係: 好比:任務A、B、C必須在任務D完成後執行。

  • 支持經過KVO監控NSOperation對象的屬性: 例如:能夠經過isCancelled屬性來判斷任務是否已取消,經過isFinished屬性來判斷任務是否已經完成等等;

  • 支持指定NSOperationQueue的優先級: 操做的優先級表示此操做與隊列中其餘操做之間的優先關係,優先級高的NSOperationQueue先執行,優先級低的後執行。GCD的隊列也有優先級,不過不是針對整個隊列的;

  • 重用NSOperation對象: 在開發中你可使用NSOperation的子類或者本身建立NSOperation對象來保存一些信息,能夠在類中定義方法,使得代碼可以屢次使用;

4、經過Dispatch Group機制,根據系統資源情況來執行任務

dispatch groupGCD的一項特性,可以把任務進行分組管理,而後等待這組任務執行完畢時會有通知,開發者能夠拿到結果真後繼續下一步操做。 另外,經過dispatch group在併發隊列上同時執行多項任務的時候,GCD會根據系統資源狀態來幫忙調度這些併發執行的任務。

5、使用dispatch_once來執行只須要運行一次的線程安全代碼

例如:咱們開發中寫一個單例,就可使用dispatch_once

+ (instancetype)sharedInstance {
    
    static Class *manager = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[Class alloc] init];
    });

    return manager;
}
複製代碼

6、不要使用dispatch_get_current_queue

理由以下:

  • dispatch_get_current_queue 函數的行爲經常與開發者所預期的不一樣,此函數已經廢棄,只應作調試之用。
  • 因爲GCD是按層級來組織的,因此沒法單用某個隊列對象來描述"當前隊列"這一律念。
  • dispatch_get_current_queue 函數用於解決由不能夠重入的代碼所引起的死鎖,而後能用此函數解決的問題,一般也能夠用"隊列特定數據"來解決。

最後,特別緻謝:《Effective Objective-C 2.0》第六章。


關注咱們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公衆號)


推薦文章:
奇舞週刊
iOS 繪製漸變·基礎篇
iOS 繪製漸變·實例篇

相關文章
相關標籤/搜索