iOS如何使用Block

本文轉(zhuǎn)載于 http://segmentfault.com/a/1190000003093017, 如有侵權(quán),請原著與我溝通,可以立即取消發(fā)布。

什么是Block


Blocks 是 iOS 4.0 之后有的新功能。
Block 能夠讓我們的代碼變得更簡單,能夠減少代碼量,降低對于 delegate 的依賴,還能夠提高代碼的可讀性。

本質(zhì)上來說,一個 Block 就是一段能夠在將來被執(zhí)行的代碼。本身 Block 就是一個普通的 Objective-C 對象。正因為它是對象,Block 可以被作為參數(shù)傳遞,可以作為返回值從一個方法返回,可以用來給變量賦值。

在其他語言(Python, Ruby, Lisp etc.)中,Block 被叫做閉包——因為他們在被聲明的時候的封裝狀態(tài)。Block 為指向它內(nèi)部的局部變量創(chuàng)造了一個常量 copy。

在 Block 之前,如果我們想要調(diào)用一段代碼,然后之后一段時間,讓它給我們返回,我們一般會使用 delegate 或者 NSNotification。這樣的寫法沒什么問題,但是使用過 delegate 和 NSNotification 大家就應(yīng)該會感覺到——我們會不可避免的將代碼寫的到處都是,我們需要在某處開始一個任務(wù),在另外一個地方來處理這個返回結(jié)果。使用 Block 就可以在一定程度上避免這個問題。

如何使用 Block


下面這張圖片來自蘋果官方文檔:

block實例

Block 的聲明格式如下

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

^ 符號其實就是專門用來表示:我們在聲明一個 Block。

聲明舉例:

int (^add)(int,int)

block 的定義格式如下

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

要注意,定義和聲明的格式有一些不同。定義以 ^ 開始,后面跟著參數(shù)(參數(shù)在這里一定要命名),順序和類型一定要和聲明中的順序一樣。定義時,返回值類型是 optional 的,我們可以在后面的代碼中確定返回值類型。如果有多個返回 statement,他們也只能有一個返回值類型,或者把他們轉(zhuǎn)成同一個類型。block 的定義舉例:

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

我們把聲明和定義放在一起:

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

調(diào)用的時候

int resultFromBlock = add(2,2);

我們將使用 block 與不使用 block 做一些對比

舉例 :NSArray
普通 for 循環(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;
}

這個 BOOL stop 現(xià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);
}];

在上面的代碼中, BOOL stop 設(shè)置為 YES 的時候,可以從block 內(nèi)部停止下一步運行。
從上面三段代碼的對比中,我們可以至少可以看出 block 兩方面的優(yōu)勢:

  • 簡化了代碼
  • 提高了速度

舉例:UIView Animation

非 Block 實現(xiàn)

-(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 實現(xiàn)

-(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 的優(yōu)勢:簡化了代碼

讓代碼保持在一起,不需要在一個地方開始動畫,在另一個地方回調(diào)。讀寫起來都比較方便。蘋果也建議這么做,不然蘋果用 block 重寫以前的代碼干嘛呢~

block 的應(yīng)用


1. enumerateObjectsUsingBlock

之前的代碼實例中已經(jīng)提到過,用來迭代數(shù)組十分方便,具體看下面的代碼實例:

-(NSArray*)retrieveInventoryItems {
    // 1 - 聲明
    NSMutableArray* inventory = [NSMutableArray new];
    NSError* err = nil;
    // 2 - 得到 inventory 數(shù)據(jù)
    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 轉(zhuǎn)化成一個 IODItem 類的對象。我們對一個JSON Array 對象發(fā)送 enumerateObjectsUsingBlock 消息,迭代這個 array,得到 item 字典,然后用這個字典得到 IODItem,最后把這些對象添加到 inventory 數(shù)組然后返回。

2.sortedArrayUsingComparator

enumerateObjectsUsingBlock我們上面已經(jīng)用過,主要來看 sortedArrayUsingComparator ,這個 block 以一個升序返回一個 array,這個升序由一個 NSComparator block 決定

注意:compare 方法的使用有點沒太明白,但是根據(jù) sortedArrayUsingComparator 是返回一個升序數(shù)組,所以compare 方法應(yīng)該是返回兩者之間更大的??


-(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 的數(shù)組,然后使用 sortedArrayUsingComparator 這個 block 方法,把這些所有的 key 按升序進行排序。

總結(jié)一些比較常用的 block


NSArray

  1. enumerateObjectsUsingBlock 這個是我最常使用的 block ,上面已經(jīng)介紹過了,用來迭代數(shù)組非常方便,個人認為這應(yīng)該是最好用的 block 了。
  2. enumerateObjectsAtIndexes:usingBlock: 和 enumerateObjectsUsingBlock 差不多,但是我們可以選擇只迭代數(shù)組的一部分,而不是迭代整個數(shù)組。這個需要迭代的范圍由 indexSet 參數(shù)傳入。
  3. indexesOfObjectsPassingTest 返回一個數(shù)組中,通過了特定的 test 的對象的 indexSet。用這個 block 來查找特定的數(shù)據(jù)很方便。

NSDictionary

  1. enumerateKeysAndObjectsUsingBlock 迭代整個字典,返回字典中所有的 key 和對應(yīng)的值(如果是想用這個 block 來代替 objectForKey 方法,確實有些多此一舉,但是如果你需要返回字典中全部的 key, value,這個 block 是一個很好的選擇)。
  2. keysOfEntriesPassingTest 和數(shù)組里的 indexesOfObjectsPassingTest block 類似,返回通過特定的 test 的一組對象的 key。

UIView Animation

animateWithDuration: animation: completion: 寫過動畫大家應(yīng)該還是比較了解的,動畫的 block 實現(xiàn)確實比非 block 實現(xiàn)簡單、方便了很多。

GCD

dispatch async:這時異步 GCD 的主要功能,在這里其實最重要的是要理解 block 是一個普通的對象,是可以作為參數(shù)傳遞給 dispatch queue 的。

使用我們自己的 block

除了使用這些系統(tǒng)提供給我們的 block,我們有時候自己寫的方法里也許也想要用到 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 對象,所以我們可以把 block 存儲在一個 property 中以便之后調(diào)用。這種方式在處理異步任務(wù)的時候特別有用,我們可以在一個異步任務(wù)完成之后存儲一個 block,之后可以調(diào)用。下面是一段示例代碼:

@property (strong) int (^mathBlock)(int, int);

// 存儲 block 以便之后調(diào)用
-(void)doMathWithBlock:(int (^)(int, int))mathBlock {
    self.mathBlock = mathBlock;
}
 
// 調(diào)用上面的方法,并傳入一個 block
-(IBAction)buttonTapped:(id)sender {
    [self doMathWithBlock:^(int a, int b) {
        return a + b;
    }];
}

 
// 結(jié)果
-(IBAction)button2Tapped:(id)sender {
    self.label.text = [NSString stringWithFormat:@"%d", self.mathBlock(3, 5)];
}

還有,我們可以使用 typedef 來簡化block 語法,當(dāng)然效果和上面的是差不多的,我們來看下面的例子:

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 !

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 第5章 引用類型(返回首頁) 本章內(nèi)容 使用對象 創(chuàng)建并操作數(shù)組 理解基本的JavaScript類型 使用基本類型...
    大學(xué)一百閱讀 3,682評論 0 4
  • iOS代碼塊Block 概述 代碼塊Block是蘋果在iOS4開始引入的對C語言的擴展,用來實現(xiàn)匿名函數(shù)的特性,B...
    smile刺客閱讀 2,468評論 2 26
  • 剽悍晨讀:如何化問題為機遇,巧妙突破人生障礙? 這本書的精髓,我覺得就是題目了,書名就是精髓《人生總有辦法》! 自...
    小秦哥哥閱讀 530評論 2 4
  • 1 許多年以后,面對廣電總局的一紙禁令,砰砰博士將會想起,他的父親帶他去見識爐火的那個遙遠的下午。當(dāng)時,布利澤德是...
    行慮閱讀 546評論 6 4
  • 廟堂高居意惶惶,英雄何必沽帝皇。 籠中寵失鎩羽翅,悔不山莊牧牛羊。
    蔚海山莊三六子閱讀 202評論 1 7

友情鏈接更多精彩內(nèi)容