Block


此處是文章概述


基礎(chǔ)使用篇

什么是Block

定義及理解

Block是C語言的擴(kuò)充功能,用一句話來概括就是:帶有局部變量的匿名函數(shù);
當(dāng)我們用面向?qū)ο蟮乃枷?,來看待Block時,又可以理解為:
Block是將函數(shù)執(zhí)行體及其上下文封裝起來的對象
那么,從上面的定義就不難理解,Block本質(zhì)是一個OC對象,這個對象封裝了一個函數(shù)的執(zhí)行體(即函數(shù)的實(shí)現(xiàn))和函數(shù)的執(zhí)行上下文(即函數(shù)的參數(shù)及返回值);
注:Block是對象這點(diǎn)在下文原理分析時會得以證明;

范式

下圖是Block的BN范式的抽象表示:


Block BN 范式.jpg

從圖中可以看出,Block由標(biāo)識符^、返回值、參數(shù)、表達(dá)式四部分組成,而從這個范式來看,Block和C語言函數(shù)極為相似,兩者只有細(xì)微的差別:C語言的函數(shù)名變成了Block的標(biāo)識符^; 除此之外,語法上基本一致,那么我們就可以拿C語言函數(shù)來類比的去理解Block的模式;
熟悉C語言的一定會知道,C語言函數(shù)中,返回值和參數(shù)都不是必須存在的部分,比如:

void func()
{
    printf("Hello World")
}

上述示例中,就省去了參數(shù)和返回值這兩個模塊,那么剩下必須要存在的模塊即為:函數(shù)名、表達(dá)式;
那么同樣來理解Block,其中Block的參數(shù)和返回值也可以不存在,那么對應(yīng)到上圖中,黑色的兩個部分 ^表達(dá)式 是必須存在的,而返回值參數(shù)是不一定存在的;

基礎(chǔ)使用及最佳實(shí)踐

在上一節(jié)中,我們看了Block的BN范式,那么這一節(jié),再來看一下Block的基礎(chǔ)使用,以及在日常使用過程中的一些最佳實(shí)踐;

基本使用

在日常開發(fā)中,如果我們想使用一個Block去處理一些邏輯,我們可以像以下那樣去使用:

int (^sum)(int a, int b) = ^ int (int a, int b){
        
        return a + b;
    };

上述例子中,我們定義了一個名稱為sum的Block,使用時只需要如下調(diào)用即可
int sums = sum(10, 20)
但是這種使用往往需要在OC的方法中定義 并且調(diào)用,超出方法作用域之后則不能再使用,故使用場景并不是太多,正因如此,才引出了下邊的作為屬性的用法;

作為屬性

將Block作為屬性調(diào)用時,不僅解決了Block作用域的問題,還可以在別的類中使用;但在使用過程中,我們需要注意當(dāng)Block作為屬性的時候,屬性關(guān)鍵字的使用:此處應(yīng)為copy;

作為參數(shù)

在日常開發(fā)中,Block作為參數(shù)使我們使用頻率最高的一種情形,例如以下情況:

- (void)startRequestWithParams:(NSDictionary *)prames completion:(void(^)(BOOL isSuccess, NSDictionary *result))completion;
[agent startRequestWithParams:prames completion:^(BOOL isSuccess, NSDictionary *result ){
    // do something 
}];

以上是一個我們常用的網(wǎng)絡(luò)請求的示例,這里將Block作為方法的參數(shù)進(jìn)行了傳遞,這樣做有以下好處:

  • 降低了代碼的分散程度
  • 集約型的處理一塊邏輯,更利于代碼查看和維護(hù)

作為返回值

在日常開發(fā)中,我們自己把Block作為返回值的情況是比較少的,但是我們也是需要去了解一下當(dāng)Block作為返回值使用時,是什么樣式;在布局庫Mssonry中大量的使用了Block作為返回值;如常用的的

- (MASConstraint * (^)(id attr))equalTo;
- (MASConstraint * (^)(CGFloat offset))offset;

那么上述兩個方法的使用,如下:

make.equalTo(someObj).offset(10)

從這里可以發(fā)現(xiàn),當(dāng)Block作為返回值時,可以很連貫的組成鏈?zhǔn)秸{(diào)用,在一些場景很適合,但在大部分場景下,Block作為返回值時寫出的代碼比較難以理解,這可能也是使用較少的一個原因吧~

最佳實(shí)踐

每個Block都有自己固定的類型,也正是因?yàn)檫@樣才可以賦值給適當(dāng)類型的變量,這個類型就是根據(jù)Block的參數(shù)返回值決定的;那么。也就有了以下這種Block的用法:

int (^sum)(int a, int b) = ^ int (int a, int b){
        
        return a + b;
    };

但是在使用時,不難發(fā)現(xiàn)這種用法,和我們在正常定義變量時有差別,Block的變量名被包含在了中間位置,放在了類型之中,這和id obj = [NSObject new] 這樣的定義方式不一樣,這樣的語法讓人難以記住,并且難以理解;
在Block定義一節(jié)有提到,Block本質(zhì)是一個OC對象,那么能不能也像對象一樣使用,為了方便使用和理解,我們可以使用定義別名的方式,給Block定義一個易于記住的別名,將Block的類型隱藏在其后面,那我們就可以使用C語言中的** 類型定義 **來解決,示例如下:

typedef int (^SumBlock)(int a, int b);

這樣一來,就不用復(fù)雜的類型來創(chuàng)建變量了,直接用新類型即可:

SumBlock sum  = ^ int (int a, int b){
        
        return a + b;
    };

這樣就符合了類型在前,變量在后的習(xí)慣了,通過這個特性,網(wǎng)絡(luò)請求的方法也可以變得簡潔起來:

typedef void (^CompletionBlock)(BOOL isSuccess, NSDictionary *result);

- (void)startRequestWithParams:(NSDictionary *)prames completion:(CompletionBlock)completion;

變量的截獲

局部變量

  • 基本數(shù)據(jù)類型:截獲其值
  • 對象類型:連同對象所有權(quán)修飾符一起截獲

靜態(tài)局部變量

以指針的形式截獲

全局變量 & 靜態(tài)全局變量

不截獲

__block關(guān)鍵字

循環(huán)引用問題


內(nèi)部原理篇

Block的本質(zhì)

Blocks捕獲變量的內(nèi)部實(shí)現(xiàn)

__block關(guān)鍵字的實(shí)現(xiàn)原理

Block存儲域


總結(jié)

此處是總結(jié)


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容