《Effective Objective-C 2.0編寫高質(zhì)量iOS與OS X代碼的52個方法》要點回顧(六)第六章 塊(block)與大中樞派發(fā)(GCD)

37. 理解 “塊” 這一概念

實例:

    /*
     
     塊(block)語法:
     return_type (^block_name) (parameters)
     
     */
    //1 全局塊
//    void (^someBlock)(void) = ^ {
//        NSLog(@"這是一個塊");
//    };
//
//    someBlock();
    //2 帶參數(shù)
//    int (^intBlock) (int a, int b) = ^(int aaa, int bbb) {
//        return aaa + bbb;
//    };
//
//    NSLog(@"%d", intBlock(900, 98));

    //3 在聲明它的范圍里,所有變量都可為其捕獲。
    int addi = 1000;
    int (^intBlock) (int a, int b) = ^(int aaa, int bbb) {
        return aaa + bbb + addi;
    };

    NSLog(@"%d", intBlock(900, 98));

    //4 內(nèi)聯(lián)塊
    
    NSArray *array = @[@0, @1, @2, @3, @4, @5];
    __block NSInteger count = 0;
    [array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        /*
         NSOrderedAscending     小于
         NSOrderedSame          等于
         NSOrderedDescending    大于
         */
        if ([obj compare:@2] == NSOrderedAscending) {
            count++;
        }
    }];
    NSLog(@"%ld", (long)count);
    
    
    //self 也是對象,塊在捕獲它時也會將其保留,所以一般block容易產(chǎn)生“保留循環(huán)”

全局塊、棧塊及堆塊

//全局塊: 由于運行該塊所需的全部信息 都能在編譯期確定,所以可以稱為“全局塊”。如下
void (^someBlock)() = ^ {
    NSLog(@"這是一個塊");
};
    
//    //棧塊
//    void (^block) ();
//
//    if (count == 2) {
//        block = ^{
//            NSLog(@"這是一個棧塊");
//        };
//    }else {
//        block = ^{
//            NSLog(@"這是一個棧塊");
//        };
//    }
//
//    block();//此處卻在堆。不安全
    
//改良
//堆塊
void (^block) ();
    
if (count == 2) {
    block = [^{
        NSLog(@"這是一個棧塊");
    } copy];
}else {
    block = [^{
        NSLog(@"這是一個棧塊");
    } copy];
}
    
block();//把塊復(fù)制到堆,就安全了

要點總結(jié)

  • 塊(block)是C、OC、C++中的詞法閉包。
  • 塊 可以接收參數(shù),也可返回值
  • 塊 可以分配到上,也可以是全局的。分配到棧上的塊可拷貝到堆里,這樣的話,就和標準的OC對象一樣,具有引用計數(shù)了。

38. 為常用的塊類型創(chuàng)建 typedef

//定義
typedef void(^EOCCompletionHandler)(NSData *data, NSError *error);
//做參數(shù)。
- (void)startWithCompletionHandler:(EOCCompletionHandler)completion;

要點總結(jié)

  • typedef重新定義 塊類型,可令塊類型用起來更加簡單。
  • 定義新類型時應(yīng)遵從現(xiàn)有的命名習慣,勿使其名稱與別的類型名稱沖突。
  • 不妨為同一個塊類型定義多種類型別名。如果要重構(gòu)的代碼中使用了塊類型的某個別名,那么只需修改響應(yīng) typedef 中的塊簽名即可,無須改動其它 typedef。

39. 用 handle 塊降低代碼分散程度

塊,有時可以代替代理(委托)方法

要點總結(jié)

  • 在創(chuàng)建對象時,可以使用 內(nèi)聯(lián)的handler塊 將相關(guān)業(yè)務(wù)一并聲明。
  • 多個實例需要監(jiān)控時,若采用委托模式,那么經(jīng)常需要根據(jù)傳入的對象來切換。而若改用 handler塊 來實現(xiàn),則可以直接將塊和相關(guān)對象放在一起。
  • 設(shè)計API時如果用到了 handler塊,那么可以增加一個參數(shù),是調(diào)用者可以通過此參數(shù)來決定 塊在哪個隊列上執(zhí)行任務(wù)。

40. 用塊引用其所屬對象時不要出現(xiàn)保留環(huán)

避免保留環(huán)的封裝方法

1:
_networkFetcher = [[EOCNetworkFetcher alloc] initWithUrl:[NSURL URLWithString:@"www.baidu.com"]];
[_networkFetcher startWithCompletionHandler:^(NSData *data, NSError *error) {
   
    _fetcherData = data;
    
    //避免保留環(huán)的方法1
    _networkFetcher = nil;
    
}];

2:
/** 避免保留環(huán)的方法2 */
- (void)p_requestCompleted {
    
    if (_handler) {
        _handler(_downloadData, nil);
    }
    self.handler = nil;
}

要點總結(jié)

  • 如果 塊所捕獲的對象 直接或間接地保留了塊本身,那么就得擔心保留環(huán)問題。
  • 一定要找個合適的時機解除保留環(huán),而不能把責任推給API的調(diào)用者。

41. 多用派發(fā)隊列,少用同步鎖

.h
{
    NSString *_someString;
}

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


/*************************************/
.m
{
    dispatch_queue_t _queue;
}
- (instancetype)init {
    if (self = [super init]) {
        //_queue = dispatch_queue_create("EOC by Wzz", nil);
        _queue = dispatch_get_global_queue(0, 0);
    }
    return self;
}


- (NSString *)someString {

    __block NSString *localSomeString;

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

    return localSomeString;
}


- (void)setSomeString:(NSString *)someString {
    dispatch_barrier_async(_queue, ^{
//        [NSThread sleepForTimeInterval:3.0f];
        _someString = someString;
    });
    
}

要點總結(jié)

  • 派發(fā)隊列(dispatch queue)可用來表述同步語義(synchronization semantic),這種做法要比使用 @synchronized塊NSLock對象 更簡單。
  • 將同步與異步派發(fā)結(jié)合起來,可以實現(xiàn)與普通加鎖機制一樣的同步行為,而這么做卻不會阻塞執(zhí)行異步派發(fā)的線程。
  • 使用同步隊列及柵欄塊,可以令同步行為更加高效。

42. 多用GCD,少用 performSelector 系列方法

要點總結(jié)

  • performSelector 系統(tǒng)方法在內(nèi)存管理方面容易有疏失。它無法確定將要執(zhí)行的選擇子具體是什么,因而 ARC 編譯器也就無法插入適當?shù)膬?nèi)存管理方法。
  • performSelctor 系列方法所能處理的選擇子太過局限了,選擇子的返回類型及發(fā)送給方法的參數(shù)個數(shù)都受到限制。
  • 如果想把任務(wù)放到另一個線程上執(zhí)行,那么最好不要用 performSelector 系列方法,而是應(yīng)該把任務(wù)封裝到塊里,然后調(diào)用大中樞派發(fā)機制(GCD)的相關(guān)方法來實現(xiàn)。

43. 掌握GCD及操作隊列的使用時機

使用 NSOperation 及 NSOperationQueue 的好處:
  • 取消某個操作。

    已啟動的任務(wù)無法取消。

  • 指定操作間的依賴關(guān)系。

  • 通過鍵值觀測機制監(jiān)控 NSOperation 對象的屬性。

  • 指定操作的優(yōu)先級

  • 重用 NSOperation 對象

要點總結(jié):

  • 在解決多線程與任務(wù)管理問題時,派發(fā)隊列并非是唯一解決方案;
  • 操作隊列提供了一套高層的 Objective-C API,能實現(xiàn)GCD所具備的絕大部門功能,而且還能完成一些更為復(fù)雜的操作,這些操作若改用GCD來實現(xiàn),需另寫代碼。

44. 通過Dispatch Group機制,根據(jù)系統(tǒng)資源狀況來執(zhí)行任務(wù)

要點總結(jié):

  • 一系列任務(wù)可歸入一個dispatch group之中。開發(fā)者可以在這組任務(wù)執(zhí)行完畢時獲得通知。
  • 通過dispatch group,可以在并發(fā)式隊列里同時執(zhí)行多項任務(wù)。此時GCD會根據(jù)系統(tǒng)資源狀況來調(diào)度這些并發(fā)執(zhí)行的任務(wù)。開發(fā)者若自己來實現(xiàn)此功能,則需要編寫大量代碼。

45. 使用dispatch_once來執(zhí)行只需執(zhí)行一次的線程安全代碼

要點總結(jié)

  • 經(jīng)常需要編寫“只需執(zhí)行一次的線程安全代碼”。通過GCD所提供的dispatch_once函數(shù),很容易就能實現(xiàn)此功能。

只需執(zhí)行一次的線程安全代碼: thread-safe single-code execution

  • 標記應(yīng)該生命在static或global作用域中。這樣的話,在把只需要執(zhí)行一次的塊傳給dispatch_once函數(shù)時,傳進入的標記也是相同的。

46. 不要使用 dispatch_get_current_queue

要點總結(jié)

  • dispatch_get_current_queue 函數(shù)的行為常常與開發(fā)者所預(yù)期的不同。此函數(shù)已被廢棄,只應(yīng)做調(diào)試只用
  • 由于派發(fā)隊列是按層級來組織的,所以無法單用某個隊列對象來描述“當前隊列”這一概念。
  • dispatch_get_current_queue 函數(shù)用于解決由不可重入的代碼所引發(fā)的死鎖,然而能用此函數(shù)解決的問題,通常也能該用“隊列特定數(shù)據(jù)”來解決。

系列文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,002評論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,400評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,136評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,714評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,452評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,818評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,812評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,997評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,552評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,292評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,510評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,721評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,121評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,429評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,235評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,480評論 2 379

推薦閱讀更多精彩內(nèi)容