Blocks 是 iOS 4.0 以後有的新功能。
Block 可以讓咱們的代碼變得更簡單,可以減小代碼量,下降對於 delegate 的依賴,還可以提升代碼的可讀性。ios
本質上來講,一個 Block 就是一段可以在未來被執行的代碼。自己 Block 就是一個普通的 Objective-C 對象。正由於它是對象,Block 能夠被做爲參數傳遞,能夠做爲返回值從一個方法返回,能夠用來給變量賦值。
在其餘語言(Python, Ruby, Lisp etc.)中,Block 被叫作閉包——由於他們在被聲明的時候的封裝狀態。Block 爲指向它內部的局部變量創造了一個常量 copy。json
在 Block 以前,若是咱們想要調用一段代碼,而後以後一段時間,讓它給咱們返回,咱們通常會使用 delegate 或者 NSNotification。這樣的寫法沒什麼問題,可是使用過 delegate 和 NSNotification 你們就應該會感受到——咱們會不可避免的將代碼寫的處處都是,咱們須要在某處開始一個任務,在另一個地方來處理這個返回結果。使用 Block 就能夠在必定程度上避免這個問題。數組
下面這張圖片來自蘋果官方文檔:
閉包
Block 的聲明格式以下:app
return_type (^block_name)(param_type, param_type, ...
^
符號其實就是專門用來表示:咱們在聲明一個 Block。
聲明舉例:異步
int (^add)(int,int)
block 的定義格式以下:async
^return_type(param_type param_name, param_type param_name, ...) { ... return return_type; }
要注意,定義和聲明的格式有一些不一樣。定義以 ^ 開始,後面跟着參數(參數在這裏必定要命名),順序和類型必定要和聲明中的順序同樣。定義時,返回值類型是 optional 的,咱們能夠在後面的代碼中肯定返回值類型。若是有多個返回 statement,他們也只能有一個返回值類型,或者把他們轉成同一個類型。
block 的定義舉例:動畫
^(int number1, int number2){ return number1+number2 }
咱們把聲明和定義放在一塊兒:spa
int (^add)(int,int) = ^(int number1, int number2){ return number1+number2; }
調用的時候:3d
int resultFromBlock = add(2,2);
咱們將使用 block 與不使用 block 作一些對比
舉例 :NSArray
普通 for 循環:
BOOL stop; for (int i = 0 ; i < [theArray count] ; i++) { NSLog(@"The object at index %d is %@",i,[theArray objectAtIndex:i]); if (stop) break; }
這個 BOOL 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); }];
在上面的代碼中, BOOL stop 設置爲 YES 的時候,能夠從block 內部中止下一步運行。
從上面三段代碼的對比中,咱們能夠至少能夠看出 block 兩方面的優點:
非 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 的優點:簡化了代碼
讓代碼保持在一塊兒,不須要在一個地方開始動畫,在另外一個地方回調。讀寫起來都比較方便。
蘋果也建議這麼作,否則蘋果用 block 重寫之前的代碼幹嗎呢~
以前的代碼實例中已經提到過,用來迭代數組十分方便,具體看下面的代碼實例:
-(NSArray*)retrieveInventoryItems { // 1 - 聲明 NSMutableArray* inventory = [NSMutableArray new]; NSError* err = nil; // 2 - 獲得 inventory 數據 NSArray* jsonInventory = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:kInventoryAddress]] options:kNilOptions error:&err]; // 3 - 使用 block 遍歷 [jsonInventory enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { NSDictionary* item = obj; [inventory addObject:[[IODItem alloc] initWithName:[item objectForKey:@"Name"] andPrice:[[item objectForKey:@"Price"] floatValue] andPictureFile:[item objectForKey:@"Image"]]]; }]; // 4 - 返回一個 inventory 的 copy return [inventory copy]; }
咱們在上面的代碼中 3 處使用了 block:
使用了 enumerateObjectsUsingBlock 方法,把一個普通的 NSDictionary 轉化成一個 IODItem 類的對象。咱們對一個JSON Array 對象發送 enumerateObjectsUsingBlock 消息,迭代這個 array,獲得 item 字典,而後用這個字典獲得 IODItem,最後把這些對象添加到 inventory 數組而後返回。
enumerateObjectsUsingBlock咱們上面已經用過,主要來看 sortedArrayUsingComparator ,這個 block 以一個升序返回一個 array,這個升序由一個 NSComparator block 決定
注意:compare 方法的使用有點沒太明白,可是根據 sortedArrayUsingComparator 是返回一個升序數組,因此compare 方法應該是返回二者之間更大的??
-(NSString*)orderDescription { // 1 - 聲明 NSMutableString* orderDescription = [NSMutableString new]; // 2 - 使用 block 進行排序 NSArray* keys = [[self.orderItems allKeys] sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { IODItem* item1 = (IODItem*)obj1; IODItem* item2 = (IODItem*)obj2; return [item1.name compare:item2.name]; }]; // 3 - 使用 block 遍歷 [keys enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { IODItem* item = (IODItem*)obj; NSNumber* quantity = (NSNumber*)[self.orderItems objectForKey:item]; [orderDescription appendFormat:@"%@ x%@\n", item.name, quantity]; }]; // 4 - 返回 return [orderDescription copy]; }
註釋2:獲得一個包含 dictionary 中全部 key 的數組,而後使用 sortedArrayUsingComparator 這個 block 方法,把這些全部的 key 按升序進行排序。
這個方法蘋果的官方說明:將一個 block 對象使用在一個字典的 entries 上(Apply an given block object to the entries of dictionary),咱們來看一段實例代碼:
-(float)totalOrder { // 1 - 定義 __block float total = 0.0; // 2 - 使用 block 計算 float (^itemTotal)(float,int) = ^float(float price, int quantity) { return price * quantity; }; // 3 - 使用 block 遍歷 dictionary [self.orderItems enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { IODItem* item = (IODItem*)key; NSNumber* quantity = (NSNumber*)obj; int intQuantity = [quantity intValue]; total += itemTotal(item.price, intQuantity); }]; // 4 - 返回 return total; }
註釋1:須要注意的是 block,由於咱們須要在 block 內部使用這個 total 變量;若是咱們不使用 block 關鍵字,下面的 block 會建立一個這個 total 變量的常量拷貝(const copy),當指向 block 內部的時候,就使用這個拷貝。這意味着在 block 咱們沒法修改它的值。但添加了這個關鍵字咱們就能夠從 block 內部讀取變量的值,或者把值寫入 block 內的變量。
註釋3:使用 enumerateKeysAndObjectsUsingBlock 能夠遍歷獲得字典裏全部的對象,以及它們對應的 key。
animateWithDuration: animation: completion: 寫過動畫你們應該仍是比較瞭解的,動畫的 block 實現確實比非 block 實現簡單、方便了不少。
dispatch async:這時異步 GCD 的主要功能,在這裏其實最重要的是要理解 block 是一個普通的對象,是能夠做爲參數傳遞給 dispatch queue 的。
除了使用這些系統提供給咱們的 block,咱們有時候本身寫的方法裏也許也想要用到 block。咱們來看一些簡單的示例代碼,這段代碼聲明瞭一個接收 block 做爲參數的方法:
-(void)doMathWithBlock:(int (^)(int, int))mathBlock { self.label.text = [NSString stringWithFormat:@"%d", mathBlock(3, 5)]; } // 如何調用 -(IBAction)buttonTapped:(id)sender { [self doMathWithBlock:^(int a, int b) { return a + b; }]; }
由於 block 就是一個 Objective-C 對象,因此咱們能夠把 block 存儲在一個 property 中以便以後調用。這種方式在處理異步任務的時候特別有用,咱們能夠在一個異步任務完成以後存儲一個 block,以後能夠調用。下面是一段示例代碼:
@property (strong) int (^mathBlock)(int, int); // 存儲 block 以便以後調用 -(void)doMathWithBlock:(int (^)(int, int))mathBlock { self.mathBlock = mathBlock; } // 調用上面的方法,並傳入一個 block -(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 來簡化block 語法,固然效果和上面的是差很少的,咱們來看下面的例子:
typedef int (^MathBlock)(int, int); // 使用 tpyedef 聲明一個property @property (strong) MathBlock mathBlock; -(void)doMathWithBlock:(MathBlock) mathBlock { self.mathBlock = mathBlock; } -(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)]; }
參考連接
Bingo !
關於 block 的使用說的差很少了,其實關於 block 最關鍵的是要理解,block 是能夠做爲參數和返回值使用得。接下來會總結一篇關於 GCD 的文章。