此處是文章概述
基礎(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由標(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é)