如何在 iOS 5 中使用 Block

這篇文章來自 iOS 教程團隊成員 Adam Burkepile, 一個全職軟件諮詢顧問和獨立 iOS 開發者。 看看他最新的 app Pocket No Agenda , 或者在 Twitter 上面關注它。 html

歡迎回到 在 iOS 5 中使用 Block 系列教程 – 咱們已經有了一些 Storyboard/Interface Builder 方面的實踐!Order up some Storyboards and Blocks in this tutorial! ios

在這個教程的第一部分,咱們用 iOS 5 中的 Storyboard 建立視圖而且創建了一個很好看的界面, 和你在上邊看到的那張截圖差很少。 程序員

在這第二部分也是這個系列的最後一部分中,咱們要使用 Block了! 咱們將會討論 Block 到底是什麼,它們的語法, 如何使用它們, 而且包含了大量的實例。 數組

咱們將會向你展現你怎樣經過 NSArray, UIView 動畫,GCD 來使用 Block, 還有其餘更多! 閉包

安排好你的時間,而且實踐性和快樂的閱讀吧。 app

 

 

開始:Block 簡介

Block 是 iOS 4.0 和 Mac OSX 10.6 引入的一個新特性。 Block 能夠極大的簡化代碼。 他們能夠幫助你減小代碼, 減小對代理的依賴, 而且寫出更加簡潔,可讀性強的代碼。 框架

即便有這麼多好處, 仍是有不少開發者沒有使用 Block, 由於他們不知道如何使用。 可是 Block 絕對是你做爲一個 Objective-C 程序員,必定會想要掌握的技能。 函數

讓咱們來看看 Block 是誰, 是什麼,在哪裏用它, 爲何用它, 還有何時用它。 動畫

Block 是什麼東西,它爲何那麼重要? ui

Why do I need these fancy block things?

Why do I need these fancy block things?

Block 的核心就是一段能夠在之後的時間裏執行的代碼。

Block 是 first-class functions, 也就是說 Block 是一個標準 Objective-C 對象。 由於他們是對象, 他們能夠做爲參數傳遞, 做爲方法或函數的返回值, 賦值給變量。

在其餘語言中,好比 Python,Ruby 和 Lisp, Block 又叫作閉包, 由於他們包含了定義時的狀態。 Block 會爲全部和它在同一做用範圍內的局部變量建立一個常量拷貝。

在沒有 Block 以前, 若是咱們想在以後的某個時間回調一個方法, 你通常會用代理或者 NSNotificationCenter。 這樣也不錯, 除了一點,它會讓你的代碼處處都是 – 你在一個地方開啓了一個任務, 而後在另一個地方處理它的結果。

Block 是很是不錯的, 由於它能將和一個任務相關的全部代碼都放在一個地方, 你立刻就會看到。

 

 

Block 爲誰準備?

你! Block 是爲每個人準備的! 嚴格的說, Block 是爲每一個人和每一個將要用到 Block 的人準備的。 Block 是將來的趨勢, 因此你最好如今也學一下。 不少內建的方法已經用 Block 重寫或者提供了接受 Block 參數的版本。

你怎樣用 Block?
這張 iOS Developer Library 中的圖片很好的解釋了 Block 的語法:

Block 的聲明格式以下:

return_type (^block_name)(param_type, param_type, ...)

 

若是你以前使用過其餘 C 類型的語言,那這段代碼你應該看起來很眼熟, 除了這個 ^ 符號。 ^ 這個符號表示了 「咱們定義的這個東西是一個 Block」。

若是你能分析到 ^ 符號的意思 「我是一個 Block」 ,那麼祝賀你 – 你已經瞭解了 Block 中最難的部分! :]

注意這裏不須要參數的名稱, 不過,若是你喜歡的話,你也能夠加上它們。

下面是定義 Block 的一個例子:

int (^add)(int,int)

下面是 Block 的定義格式:

// Block Definition ^return_type(param_type param_name, param_type param_name, ...) { ... return return_type; }

這就是 Blcok 實際是怎麼建立的。

 Block 還有另一種不一樣的定義方法。 以 ^ 符號起始,後面跟隨着參數,這裏的參數必須有參數名, 還必須和它要賦值到的 Block 聲明中參數列表裏面的參數類型和順序相匹配。下面是實際的代碼。

當你定義 Block 時, 返回值類型是可選的,而且能夠繼承它裏面代碼的返回值類型。 若是它裏面有多條 return 語句,那麼這些語句返回的類型必須都是相同的 (或者強制轉換到相同的類型)。

這裏是 Block 定義的一個例子:

^(int number1, int number2){ return number1+number2 }

 

若是咱們將 Block 的聲明和定義放在一塊兒, 咱們會獲得這樣一個語句:

int (^add)(int,int) = ^(int number1, int number2){ return number1+number2; }

咱們能夠這樣使用 Block:

int resultFromBlock = add(2,2);

讓咱們看一看,使用 Block 和不使用 Block 之間對比的一些例子

 

示例: NSArray

讓咱們看看 Block 如何改變咱們操做數組的方式。

首先,讓咱們看一下通常狀況下處理循環的方式:

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」 變量能讓你在任什麼時候候中止循環,咱們簡單的複製了這個功能來支持和 Block 的方式等同的效果。

如今,讓咱們看看用快速枚舉的方法實現一樣的功能:

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, 這樣就後續的任何循環都不會繼續了。 這是傳遞到 enumerateObjectsUsingBlock 方法的 Block 中的其中一個參數。

上面這個例子有些微不足道, 並且也很難明顯的體現出 Block 所到來的好處。 可是我想給你們指出 Block 的兩點好處:

  1. 簡單性. 使用 Block 咱們能夠不寫任何附加的代碼就能夠訪問對象,對象在數組中的索引,stop 變量。 這意味着少許的代碼,減小了發生編碼錯誤的機會(固然,並不是咱們必定會出現編碼錯誤)。
  2. 速度. 使用 Block 在執行速度上要比使用快速枚舉快。 在咱們這個例子中,這點微小的速度提高不值得一提,可是在更復雜的狀況下,這個優點就愈來愈重要。(來源)

 

 

示例: UIView Animation

讓咱們對一個單獨的 UIView 執行一個簡單的動畫。 它將視圖的透明度調整爲 0,將這個視圖向下和向右移動 50 點。 而後將這個 UIView 從它的 superview 中刪除掉, 很簡單,對嗎?

非 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]; }];
}

若是咱們仔細看一看這兩個方法, 就會發現有 3 個優點:

  1. 更簡單的代碼 使用 Block, 咱們再也不須要單獨定義一個回調方法, 或者調用 beginAnimations/commitAnimations 。
  2. 保持代碼在一塊兒 使用 Block, 咱們再也不須要在一個地方開啓動畫,而後再另一個地方處理回調。 全部和咱們動畫相關的代碼都在一處, 這樣讓他的可讀性和維護性更強。
  3. 蘋果推薦這樣 這裏有一個現實的例子, 蘋果已經用 Block 重寫了以前的一些功能, 如今蘋果官方也推薦,若是可能的話,儘可能遷移到基於 Block 的方法上面。
  4. 何時用 Blocks

    我認爲最佳的建議是, 在最合適用 Block 的地方使用它。 這裏你可能會出於向後兼容或者更加熟悉之前的方式的緣由,從而還要用老的方法。
    可是每次你面臨這種決策的時候, 想想 Block 是否能讓你更輕鬆以及你是否能用基於 Block 的方式代替現有代碼。 而後選擇一個對你最有價值的方法。

    固然,你可能會發現,在之後的時間裏, 你須要愈來愈多的使用 Block, 由於大多數框架,不管是第三方的仍是蘋果本身的,都正在用 Block 重寫。因此爲了讓你將來更加輕鬆,如今就開始使用 Block 吧