**Block **是iOS在4.0之后新增的語(yǔ)法蛹磺,在iOS SDK 4.0之后,block幾乎出現(xiàn)在所有新版的API之中,換句話說(shuō)笛谦,如果不了解block這個(gè)概念就無(wú)法使用SDK 4.0版本以后的新功能碗暗,所以我們有必要去學(xué)習(xí)下block的使用了颈将,英文比較好的童鞋可以自行參照官方文檔,本文中的示例采摘于官方文檔.
-
Block的定義
在這一小節(jié)我們先用一些簡(jiǎn)單范例來(lái)引入block的概念言疗。
1.1 聲明和創(chuàng)建Block:
我們使用「^」運(yùn)算子來(lái)聲明一個(gè)block晴圾,而且在block的定義最后面要加上「;」來(lái)表示一個(gè)完整的述句,下面是一個(gè)block的范例:
int multiplier = 7;
int (^myBlock)(int) = ^(int num){
return num * multiplier;
};
例子中我們聲明了一個(gè)myBlock的代碼塊噪奄,用「^」符號(hào)來(lái)表示這是一個(gè)block死姚。聲明告訴我們myBlock是一個(gè)入?yún)⒑统鰠⒍紴檎停╥nt)的block。
值得注意的地方是block可以使用和本身定義范圍相同的變數(shù)勤篮,在上面的例子中multiplier 和myBlock 都是某一個(gè)函數(shù)內(nèi)定義的兩個(gè)變量都毒,都在某個(gè)函數(shù)兩個(gè)大括號(hào)「{」和「 }」中間的區(qū)塊,因此它們的有效范圍是相同的碰缔,所以在block中就可以直接使用 multiplier 這個(gè)變數(shù)账劲,此外當(dāng)把block定義成一個(gè)變量的時(shí),我們可以直接像使用一般函數(shù)般的方式使用它:
int multiplier = 7 ;
int (^myBlock)( int ) = ^( int num){
return num * multiplier;
};
printf ( "%d" , myBlock( 3 ));
//結(jié)果會(huì)打印出21
1.2 直接使用Block
在很多情況下,我們并不需要將block聲明為變量涤垫,反之我們可以直接在需要使用block的地方直接用內(nèi)嵌的方式將block的內(nèi)容寫出來(lái)姑尺,在下面的例子中AFNet的一個(gè)函數(shù),就是直接使用block做為它的參數(shù):
- (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
}
1.3 __block 變量
一般來(lái)說(shuō)蝠猬,在block內(nèi)只能讀取在同一個(gè)作用域的變量而且沒(méi)有辦法修改在block外定義的任何變量切蟋,如果我們想要這些變量能夠在block中被修改,就必須在前面掛上__block的修飾詞榆芦,以上面第一個(gè)例子中的 multiplier 來(lái)說(shuō)柄粹,這個(gè)變數(shù)在 block 中是只讀的,所以 multiplier = 7 指定完后匆绣,在 block 中的 multiplier 就只能是 7 不能修改驻右,若我們?cè)?block 中修改 multiplier,在編譯時(shí)就會(huì)報(bào)錯(cuò)崎淳,因此若要在 block 中修改 multiplier 堪夭,就必須在 multiplier 前面加上__block的修飾詞,請(qǐng)參考下面的范例:
__block int multiplier = 7;
int (^myBlock)(int) = ^(int num){
if (num > 5 ) {
multiplier = 7 ;
}
else {
multiplier = 10 ;
}
return num * multiplier;
};
printf ( "%d" , myBlock( 3 ));
//結(jié)果會(huì)打印出30
-
Block概要
Block 提供我們一種能夠?qū)⒑瘮?shù)程式碼內(nèi)嵌在一般述句中的方法拣凹,在其他語(yǔ)言中也有類似的概念稱做「closure」森爽,也就是通常我們說(shuō)的閉包的概念。
2.1 Block 的功能
Block 是一種具有匿名功能的內(nèi)嵌函數(shù)嚣镜,它的特性如下: 如一般的函數(shù)般能擁有帶有型態(tài)的參數(shù)爬迟。擁有回傳值【漳洌可以擷取被定義的詞法作用域(lexical scope)狀態(tài)付呕。可以選擇性地修改詞法作用域的狀態(tài)跌捆。
注:詞法作用域(lexical scope)可以想像成是某個(gè)函數(shù)兩個(gè)大括號(hào)中間的區(qū)塊徽职,這個(gè)區(qū)塊在程式執(zhí)行時(shí),系統(tǒng)會(huì)將這個(gè)區(qū)塊放入堆疊記憶體中佩厚,在這個(gè)區(qū)塊中的宣告的變數(shù)就像是我們常聽到的區(qū)域變數(shù)姆钉,當(dāng)我們說(shuō)block可以擷取同一詞法作用域的狀態(tài)時(shí)可以想像block變數(shù)和其他區(qū)域變數(shù)是同一個(gè)層級(jí)的區(qū)域變數(shù)(位于同一層的堆疊里),而block的內(nèi)容可以讀取到和他同一層級(jí)的其他區(qū)域變數(shù)可款。我們可以拷貝一個(gè)block育韩,也可以將它丟到其他的執(zhí)行緒中使用,基本上雖然block在iOS程式開發(fā)中可以使用在C/C++開發(fā)的程式片段闺鲸,也可以在Objective-C中使用筋讨,不過(guò)在系統(tǒng)的定義上,block永遠(yuǎn)會(huì)被視為是一個(gè)Objective-C的物件摸恍。
2.2 Block 的使用時(shí)機(jī)
Block 一般是用來(lái)表示悉罕、簡(jiǎn)化一小段的程式碼赤屋,它特別適合用來(lái)建立一些同步執(zhí)行的程式片段、封裝一些小型的工作或是用來(lái)做為某一個(gè)工作完成時(shí)的回傳呼叫(callback) 壁袄。在新的iOS API中block被大量用來(lái)取代傳統(tǒng)的delegate和callback类早,而新的API會(huì)大量使用block主要是基于以下兩個(gè)原因:
一、可以直接在程式碼中撰寫等會(huì)要接著執(zhí)行的程式嗜逻,直接將程式碼變成函數(shù)的參數(shù)傳入函數(shù)中涩僻,這是新API最常使用block的地方。二栈顷、可以存取區(qū)域變數(shù)逆日,在傳統(tǒng)的callback實(shí)作時(shí),若想要存取區(qū)域變數(shù)得將變數(shù)封裝成結(jié)構(gòu)才能使用萄凤,而block則是可以很方便地直接存取區(qū)域變數(shù)室抽。
-
聲明和創(chuàng)建Block
3.1 聲明一個(gè)Block變量的參考
// 入?yún)榭?出數(shù)也為空的block
void(^blockReturnVoidWithVoidArgument)(void);
// 出參為整型(int),兩個(gè)入?yún)⒎謩e是整型(int)和字符類型(char)的block
int(^blockReturnIntWithIntAndCharArguments)(int,char);
//出參為空靡努,含有10個(gè)block的數(shù)組坪圾,每個(gè)block都有一個(gè)類型為int的入?yún)?void(^arrayOfTenBlocksReturnVoidWinIntArgument[10])(int);
3.2 創(chuàng)建一個(gè)Block
我們使用「^」聲明一個(gè)block,并在最后使用「;」來(lái)表示結(jié)束惑朦,下面的范例示范了一個(gè)block變量兽泄,然后再定義一個(gè)block把它指定給block變量:
/* 聲明block 變量*/
int (^oneBlock)(int);
/* 定義一個(gè)block 并指定給上面聲明的變量*/
oneBlock = ^(int anInt)
{
return anInt = - 1 ;
};
3.3 全局的Block
聲明一個(gè)全局的block行嗤,請(qǐng)參考以下范例:
int GlobalInt = 0;
int(^getGlobalInt)(void) = ^(void){
return GlobalInt;
}
-
Block和變量
4.1 變量的形態(tài)
我們可以在block中遇到平常在函數(shù)中會(huì)遇到的變量類型:
全域(global)變量或是靜態(tài)的區(qū)域變量(static local)已日。
全域的函數(shù)垛耳。
區(qū)域變量和由封閉領(lǐng)域(enclosing scope)傳入的參數(shù)栅屏。
除了上述之外block額外支援了另外兩種變量:在函數(shù)內(nèi)可以使用**__block** 變量,這些變量在block中是可被修改的堂鲜。匯入常數(shù)(const imports)栈雳。
此外,在方法的實(shí)作里缔莲,block可以使用Objective-C的實(shí)體變量(instance variable)哥纫。
下列的規(guī)則可以套用到在block中變量的使用:
可以存取全域變量和在同一領(lǐng)域(enclosing lexical scope)中的靜態(tài)變量。
可以存取傳入block的參數(shù)(使用方式和傳入函數(shù)的參數(shù)相同)痴奏。
在同一領(lǐng)域的區(qū)域變數(shù)在block中將視為常數(shù)(const)蛀骇。
可以存取在同一領(lǐng)域中以__block 為修飾詞的變數(shù)。
在block中宣告的區(qū)域變數(shù)读拆,使用方式和平常函數(shù)使用區(qū)域變數(shù)的方式相同擅憔。
下面的例子介紹了區(qū)域變數(shù)(上述第三點(diǎn))的使用方式:
int x = 123;
void (^printXandY)(int) = ^(int y)
{
printf("%d##%d",x,y);
}
//printXAndY(456);將會(huì)輸出123##456
4.2 __block 型態(tài)變量
我們可以藉由將一個(gè)由外部匯入block的變量放上修飾詞__block來(lái)讓這個(gè)變數(shù)由只讀變成讀寫,不過(guò)有一個(gè)限制就是傳入的變量在記憶體中必須是一個(gè)占有固定長(zhǎng)度記憶體的變數(shù)__block修飾詞無(wú)法使用于像是變動(dòng)長(zhǎng)度的陣列這類不定長(zhǎng)度的變數(shù)檐晕,請(qǐng)參考下面的范例:
//加上__block 修飾詞暑诸,所以可以在block 中被修改蚌讼。
__block int x = 123;
void (^printXandY)(int) = ^(int y){
x = x+y;
printf("%d %d",x,y);
}
printXAndY( 456 ); // 將會(huì)印出579 456
下面我們使用一個(gè)范例來(lái)介紹各類型的變數(shù)和block之間的互動(dòng):
extern NSInteger CounterGlobal;
static NSInteger CounterStatic;
{
NSInteger localCounter = 42 ;
__block char localCharacter;
void(^aBlock)(void) = ^(void){
++ CounterGlobal; //可以存取
++ CounterStatic; //可以存取
CounterGlobal = localCounter; //localCounter在block 建立時(shí)就不可變了
localCharacter = 'a' ; //設(shè)定外面定義的localCharacter 變量
}
++localCounter; //不會(huì)影響的block 中的值
localCharacter = 'b';
aBlock(); //執(zhí)行block 的內(nèi)容
//執(zhí)行完后,localCharachter 會(huì)變成'a'
}
4.3 block 中的引用計(jì)數(shù)問(wèn)題
我們?cè)赽lock中引用外部的變量个榕,在一般的情況下它將會(huì)自動(dòng)增加變量的引用計(jì)數(shù)篡石,不過(guò)若以__block作為修飾,引用計(jì)數(shù)不受影響西采,因此我們需要注意兩點(diǎn)
』巳1. 若直接引用實(shí)例變量(instance variable),self的引用計(jì)數(shù)加1械馆。
」得铩2.若通過(guò)變量存取實(shí)例變數(shù)的值,只是實(shí)例變量的引用計(jì)數(shù)加1狱杰。
以下范例說(shuō)明上面兩種情況瘦材,假設(shè)instanceVariable是實(shí)體變量:
dispath_async(queue,^{
//因?yàn)橹苯哟嫒?shí)體變量instanceVariable ,所以self 的retain count會(huì)加1
doSomethingWithObject (instanceVariable);
});
id localVaribale = instanceVariable;
dispatch_async(queue,^{
//localVariable是存取值仿畸,所以這時(shí)只有l(wèi)ocalVariable 的retain count 加1
//self 的return count并不會(huì)增加食棕。
doSomethingWithObject (localVaribale);
});
-
使用Block
5.1 直接使用
我們可以像使用一般函數(shù)的方式來(lái)使用它,請(qǐng)參考下面兩個(gè)范例:
int(^aBlock)(int) = ^(int aInt){
return aInt-1;
};
printf("10 minus 1 is %d", aBlock(10));
//結(jié)果會(huì)顯示:10 minus 1 is 9
float(^distanceTraveled)(float, float, float) = ^(float startingSpeed, float acceleration, float time){
float distance = (startingSpeed * time) + ( 0.5 * acceleration * time * time);
return distance;
};
float howFar = distanceTraveled( 0.0 , 9.8 , 1.0 );
//howFar的值為4.9
在一般常見的情況中错沽,若是將block當(dāng)做是參數(shù)傳入函數(shù)簿晓,我們通常會(huì)使用「內(nèi)嵌」的方式來(lái)使用block。
5.2 將Block當(dāng)作函數(shù)的參數(shù)
我們可以像一般函數(shù)使用參數(shù)的方式千埃,將block以函數(shù)參數(shù)的形式傳入函數(shù)中憔儿,在這種情況下,大多數(shù)我們使用block的方式將不會(huì)傾向聲明block而是直接以內(nèi)嵌的方式來(lái)將block傳入放可,這也是目前SDK中主流的做法:
char *myCharacters[3]={"TomJohn","George","Charles Condomine"};
qsort_b(myCharacters, 3, sizeof(char *),^(const void *l,const void *r){
char *left = *(char **)l;
char *right = *( char **)r;
return strncmp (left, right, 1 );
}//這里是block 的終點(diǎn)谒臼。
);
//最后的結(jié)果為:{"Charles Condomine", "George", "TomJohn"}