本文部分內(nèi)容摘自。
1.Block是啥?憑什么這么屌?
就本質(zhì)來說,一個Block就是一大堆在接下來某個時間可以被執(zhí)行的代碼。
Block 是一等函數(shù)(first-class function)[1]。一等函數(shù)!這個夢幻般的爵位(如果計算機科學也有爵位的話~),卻揭露了它丫的就是Objective-C的一個常規(guī)對象。因為它也就是個對象,所以它可以作為參數(shù)被傳來傳去,被方法和函數(shù)作為返回值送人,也可以被指定為變量。
Block在一些諸如Python、Ruby、Lisp這樣的語言中被叫做閉包,因為在它被聲明的時候,它肚子里就已然有貨了。一個block會為它范圍內(nèi)的任一變量做個copy,讓小弟吃飽穿暖,別稀里糊涂的生命周期就結束了。
在這貨出現(xiàn)之前,你想完成回調(diào),最典型的就是用delegate或者NSNotificationCenter。這兩個家伙表現(xiàn)挺好的,不過,卻讓你的代碼到處跑,你從一個地方開始任務,卻要在另一個地方處理結果。
Block就機靈得多,在處理任務的時候,它讓你不挪窩,就把事兒給辦了,就像你接下來將要看到的。
2.Block為誰而生?
就是你們!不騙你們,Block是位每一個苦逼程序猿創(chuàng)造的,每個人都該用。Block代表了未來,所以你現(xiàn)在就得學。很多內(nèi)置框架都基于這貨重寫或擴展了。
3.Block怎么用?
下面那張妖里妖氣的圖片,來自蘋果開發(fā)者庫,做了很好的Block句法解釋。

我從左到右,從上到下翻譯一下:
- 我們聲明了一個叫“myBlock”的變量,“^”告訴我們這是一個block;
- 這是一個字面量block的定義,指派給了“myBlock”;
- "myBlock"是一個返回整型值的block;
- 它只有一個整型參數(shù);
- 參數(shù)名字叫“num”;
- 這是block的貨,也就是block的實現(xiàn)。
Block的聲明形式如下:
return_type (^block_name)(param_type, param_type, ...)
如果你有C系語言的編程經(jīng)驗的話,這會讓你似曾相識,除了^,^表明“這貨正在聲明一個block”。
如果你腦海中縈繞著:^表示“我是一個block”,恭喜——你已經(jīng)學會使用block最難的部分。(我讀書少,你可不要騙我啊...)
注意,在聲明的時候,給參數(shù)命名不是必要的(只要知道是什么類型就行了),如果你愿意可以加上。
以下是聲明一個block的例子:
int (^add)(int,int)
接著,是block的定義:
// Block Definition
^return_type(param_type param_name, param_type param_name, ...) { ... return return_type; }
這就是Block的創(chuàng)建形式。要注意,Block的定義和聲明有不同的結構。定義是以^開頭,后面緊跟著參數(shù),這次參數(shù)要有名字啦,并且它們的順序要和Block定義中的一樣。
當定義Block的時候,返回值類型可以不填,因為它可以根據(jù)代碼推斷出來。如果有多個返回的語句,那每個語句返回的必須是同一類型的(或者強轉成相同的類型)。
以下是一個Block定義的例子:
^(int number1, int number2){ return number1+number2 }
如果我們把Block聲明和定義的例子組合在一塊兒,我們得到一個完整的語句:
int (^add)(int,int) = ^(int number1, int number2){
return number1+number2;
}
然后我們可以這樣使用:
int resultFromBlock = add(2,2);
接下來,我們讓用了Block的,和沒用Block的代碼PK一下!
例子:NSArray
我們看看Block怎樣改變了我們操作數(shù)組。
首先,我們看一個常規(guī)的循環(huán):
BOOL stop;
for (int i = 0 ; i < [theArray count] ; i++) {
NSLog(@"The object at index %d is %@",i,[theArray objectAtIndex:i]);
if (stop)
break;
}
上面方法的stop變量可能讓你覺得沒什么意義。但是,當你看到同一方法但基于Block,它將變得更明晰。Block方式提供一個stop變量,使你能夠隨時停止循環(huán)。我們剛才簡單復制了一些與Block功能相同的代碼。
現(xiàn)在我們再看一個用快速枚舉的代碼:
BOOL stop;
int idx = 0;
for (id obj in theArray) {
NSLog(@"The object at index %d is %@",idx,obj);
if (stop)
break;
idx++;
}
最后我們用Block:
[theArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
NSLog(@"The object at index %d is %@",idx,obj);
}];
在上面的Block中,你可能好奇stop變量是干嘛的。它是一個簡單變量,可以在Block中被置為YES來停止進一步的處理。
上面的例子有點不知所云,而且很難看出Block有嘛好處(擦~,那你為什么這么寫...)。但是,有兩點是我要指出的:
- 簡單粗暴。用Block,我們有機會操作數(shù)組里的對象,對象的索引,和一個布爾變量,不用謝任何代碼。而更少的代碼以為著更少的錯誤。
- 唯快不破。用Block比快速枚舉要有哦輕微的速度上的優(yōu)勢。在此例中,這速度上的優(yōu)勢(可能存在)是如此之小根本沒法發(fā)覺,但在復雜的案例中,這個優(yōu)勢是很重要的(請看官們停止爆粗口吧,作者寫到這里,可能也有點惴惴不安,所以給自己提供了一個證據(jù),來證明自己不是大忽悠...)。
例子:UIView 動畫
讓我們操作一個UIView的簡單動畫。這個動畫就是:把一個UIView從superview上刪掉的時候,讓它的透明度變?yōu)?,并且x、y各增加50。簡單不?
不用Block的方式:
- (void)removeAnimationView:(id)sender {
[animatingView removeFromSuperview];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[UIView beginAnimations:@"Example" context:nil];
[UIView setAnimationDuration:5.0];
[UIView setAnimationDidStopSelector:@selector(removeAnimationView)];
[animatingView setAlpha:0];
[animatingView setCenter:CGPointMake(animatingView.center.x+50.0,
animatingView.center.y+50.0)];
[UIView commitAnimations];
}
用Block的方式:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[UIView animateWithDuration:5.0
animations:^{
[animatingView setAlpha:0];
[animatingView setCenter:CGPointMake(animatingView.center.x+50.0,
animatingView.center.y+50.0)];
}
completion:^(BOOL finished) {
[animatingView removeFromSuperview];
}];
}
觀察這兩個方法,對我來說,Block有3個突出的優(yōu)點:
- 代碼簡潔。用Block,我們不必聲明每一個單獨的方法來完成回調(diào)。
- 代碼集中。用Block,我們不用在一個地方開始動畫,在另一個地方執(zhí)行回調(diào),所有與動畫相關的,都在一起,使代碼更加可讀可寫。
- 蘋果大哥這么說的...這就是一個蘋果用Block重寫已經(jīng)存在的功能的例子。現(xiàn)在蘋果官方建議每位過渡到基于Block的方法(證據(jù))。
4.Block什么時候用?
(誰說外國人不會說官話,請欣賞,咳咳,難道是華裔)
我認為我能給出的最好的建議是,當你覺得合適的時候,你就該用了(hehe~)。很有可能你還想用你以前的方法,因為代碼維護啊、適配啊,你用以前的方法更得心應手。但是每次你都要想想,是否Block能簡化你的生活,是否可以用基于Block的方法來代替。然后選擇最優(yōu)的。
當然,你會發(fā)現(xiàn)你在將來需要用越來越多的Block,因為很多框架,無論是第三方的,或者蘋果自己的,已經(jīng)用Block寫了或者重寫了很多方法。所以現(xiàn)在開始就用Block吧,這樣在面對未來的時候,你才能算有備而來(一句話,形勢比人強,程序猿,看著辦吧)。
5.創(chuàng)建自己的Block
Block的好,已經(jīng)說半天了,你肯定已經(jīng)迫不及待的想寫一些Block了,下面給一些代碼片段:
// 這是一個用Block做參數(shù)的方法
- (void)doMathWithBlock:(int (^)(int, int))mathBlock {
self.label.text = [NSString stringWithFormat:@"%d", mathBlock(3, 5)];
}
// 調(diào)用上面的方法
- (IBAction)buttonTapped:(id)sender {
[self doMathWithBlock:^(int a, int b) {
return a + b;
}];
}
因為Block就是一個Objective-C的對象,所以你可以把它作為一個屬性,以便你再調(diào)用。這在回調(diào)中相當有用。以下是一個例子:
// 聲明一個屬性
@property (strong) int (^mathBlock)(int, int); // 不用ARC的話,“strong”改為“copy”
// 將方法參數(shù)中的block儲存到屬性,方便以后調(diào)用
- (void)doMathWithBlock:(int (^)(int, int))mathBlock {
self.mathBlock = mathBlock;
}
// 調(diào)用方法
- (IBAction)buttonTapped:(id)sender {
[self doMathWithBlock:^(int a, int b) {
return a + b;
}];
}
// 接下來就可以用屬性了...
- (IBAction)button2Tapped:(id)sender {
self.label.text = [NSString stringWithFormat:@"%d", self.mathBlock(3, 5)];
}
最后,你可以用typedef簡化一下句法。我們把上面的例子改造一下:
// 用typedef創(chuàng)建一種Block類型
typedef int (^MathBlock)(int, int);
// 用此類型定義一個屬性
@property (strong) MathBlock mathBlock;
// 將方法參數(shù)中的block儲存到屬性,方便以后調(diào)用
- (void)doMathWithBlock:(MathBlock) mathBlock {
self.mathBlock = mathBlock;
}
// 調(diào)用方法
- (IBAction)buttonTapped:(id)sender {
[self doMathWithBlock:^(int a, int b) {
return a + b;
}];
}
// 接下來就可以用屬性了...
- (IBAction)button2Tapped:(id)sender {
self.label.text = [NSString stringWithFormat:@"%d", self.mathBlock(3, 5)];
}
6.Block和DelegateDemo
最后,如果你想比較一下Block和Delegate實現(xiàn)回調(diào)的異同,這里有我的一個簡單Demo。
-
在計算機科學中,如果一個編程語言把函數(shù)當作一等公民,那么就說它有一等函數(shù)。具體來說,該語言支持把函數(shù)當作一個參數(shù)來傳遞、可以作為其他函數(shù)的返回值來返回、可以把它定義為變量,同時,也可以把它作為一種數(shù)據(jù)結構來存儲。(翻譯自維基百科·
First-class funcion) ?