iOS如何使用Block

本文轉(zhuǎn)載于 http://segmentfault.com/a/1190000003093017, 如有侵權(quán)涉馁,請(qǐng)?jiān)c我溝通,可以立即取消發(fā)布峭火。

什么是Block


Blocks 是 iOS 4.0 之后有的新功能毁习。
Block 能夠讓我們的代碼變得更簡(jiǎn)單,能夠減少代碼量卖丸,降低對(duì)于 delegate 的依賴(lài)纺且,還能夠提高代碼的可讀性。

本質(zhì)上來(lái)說(shuō)稍浆,一個(gè) Block 就是一段能夠在將來(lái)被執(zhí)行的代碼载碌。本身 Block 就是一個(gè)普通的 Objective-C 對(duì)象。正因?yàn)樗菍?duì)象衅枫,Block 可以被作為參數(shù)傳遞嫁艇,可以作為返回值從一個(gè)方法返回,可以用來(lái)給變量賦值弦撩。

在其他語(yǔ)言(Python, Ruby, Lisp etc.)中步咪,Block 被叫做閉包——因?yàn)樗麄冊(cè)诒宦暶鞯臅r(shí)候的封裝狀態(tài)。Block 為指向它內(nèi)部的局部變量創(chuàng)造了一個(gè)常量 copy益楼。

在 Block 之前猾漫,如果我們想要調(diào)用一段代碼,然后之后一段時(shí)間感凤,讓它給我們返回悯周,我們一般會(huì)使用 delegate 或者 NSNotification。這樣的寫(xiě)法沒(méi)什么問(wèn)題俊扭,但是使用過(guò) delegate 和 NSNotification 大家就應(yīng)該會(huì)感覺(jué)到——我們會(huì)不可避免的將代碼寫(xiě)的到處都是,我們需要在某處開(kāi)始一個(gè)任務(wù)坠陈,在另外一個(gè)地方來(lái)處理這個(gè)返回結(jié)果萨惑。使用 Block 就可以在一定程度上避免這個(gè)問(wèn)題。

如何使用 Block


下面這張圖片來(lái)自蘋(píng)果官方文檔:

block實(shí)例

Block 的聲明格式如下

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

^ 符號(hào)其實(shí)就是專(zhuān)門(mén)用來(lái)表示:我們?cè)诼暶饕粋€(gè) Block仇矾。

聲明舉例:

int (^add)(int,int)

block 的定義格式如下

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

要注意庸蔼,定義和聲明的格式有一些不同。定義以 ^ 開(kāi)始贮匕,后面跟著參數(shù)(參數(shù)在這里一定要命名)姐仅,順序和類(lèi)型一定要和聲明中的順序一樣。定義時(shí),返回值類(lèi)型是 optional 的掏膏,我們可以在后面的代碼中確定返回值類(lèi)型劳翰。如果有多個(gè)返回 statement,他們也只能有一個(gè)返回值類(lèi)型馒疹,或者把他們轉(zhuǎn)成同一個(gè)類(lèi)型佳簸。block 的定義舉例:

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

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

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

調(diào)用的時(shí)候

int resultFromBlock = add(2,2);

我們將使用 block 與不使用 block 做一些對(duì)比

舉例 :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;
}

這個(gè) BOOL stop 現(xiàn)在看上去有點(diǎn)奇怪,但看到后面 block 實(shí)現(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 的時(shí)候生均,可以從block 內(nèi)部停止下一步運(yùn)行。
從上面三段代碼的對(duì)比中腥刹,我們可以至少可以看出 block 兩方面的優(yōu)勢(shì):

  • 簡(jiǎn)化了代碼
  • 提高了速度

舉例:UIView Animation

非 Block 實(shí)現(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 實(shí)現(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)勢(shì):簡(jiǎn)化了代碼

讓代碼保持在一起马胧,不需要在一個(gè)地方開(kāi)始動(dòng)畫(huà),在另一個(gè)地方回調(diào)衔峰。讀寫(xiě)起來(lái)都比較方便佩脊。蘋(píng)果也建議這么做,不然蘋(píng)果用 block 重寫(xiě)以前的代碼干嘛呢~

block 的應(yīng)用


1. enumerateObjectsUsingBlock

之前的代碼實(shí)例中已經(jīng)提到過(guò)朽色,用來(lái)迭代數(shù)組十分方便邻吞,具體看下面的代碼實(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 - 返回一個(gè) inventory 的 copy
    return [inventory copy];
}

我們?cè)谏厦娴拇a中 3 處使用了 block:

使用了 enumerateObjectsUsingBlock 方法,把一個(gè)普通的 NSDictionary 轉(zhuǎn)化成一個(gè) IODItem 類(lèi)的對(duì)象葫男。我們對(duì)一個(gè)JSON Array 對(duì)象發(fā)送 enumerateObjectsUsingBlock 消息抱冷,迭代這個(gè) array,得到 item 字典梢褐,然后用這個(gè)字典得到 IODItem旺遮,最后把這些對(duì)象添加到 inventory 數(shù)組然后返回。

2.sortedArrayUsingComparator

enumerateObjectsUsingBlock我們上面已經(jīng)用過(guò)盈咳,主要來(lái)看 sortedArrayUsingComparator 耿眉,這個(gè) block 以一個(gè)升序返回一個(gè) array,這個(gè)升序由一個(gè) NSComparator block 決定

注意:compare 方法的使用有點(diǎn)沒(méi)太明白鱼响,但是根據(jù) sortedArrayUsingComparator 是返回一個(gè)升序數(shù)組鸣剪,所以compare 方法應(yīng)該是返回兩者之間更大的?丈积?


-(NSString*)orderDescription {
        // 1 - 聲明
    NSMutableString* orderDescription = [NSMutableString new];
        // 2 - 使用 block 進(jìn)行排序
    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:得到一個(gè)包含 dictionary 中所有 key 的數(shù)組筐骇,然后使用 sortedArrayUsingComparator 這個(gè) block 方法,把這些所有的 key 按升序進(jìn)行排序江滨。

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


NSArray

  1. enumerateObjectsUsingBlock 這個(gè)是我最常使用的 block 铛纬,上面已經(jīng)介紹過(guò)了,用來(lái)迭代數(shù)組非常方便唬滑,個(gè)人認(rèn)為這應(yīng)該是最好用的 block 了告唆。
  2. enumerateObjectsAtIndexes:usingBlock: 和 enumerateObjectsUsingBlock 差不多棺弊,但是我們可以選擇只迭代數(shù)組的一部分,而不是迭代整個(gè)數(shù)組擒悬。這個(gè)需要迭代的范圍由 indexSet 參數(shù)傳入模她。
  3. indexesOfObjectsPassingTest 返回一個(gè)數(shù)組中,通過(guò)了特定的 test 的對(duì)象的 indexSet茄螃。用這個(gè) block 來(lái)查找特定的數(shù)據(jù)很方便缝驳。

NSDictionary

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

UIView Animation

animateWithDuration: animation: completion: 寫(xiě)過(guò)動(dòng)畫(huà)大家應(yīng)該還是比較了解的,動(dòng)畫(huà)的 block 實(shí)現(xiàn)確實(shí)比非 block 實(shí)現(xiàn)簡(jiǎn)單吻氧、方便了很多溺忧。

GCD

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

使用我們自己的 block

除了使用這些系統(tǒng)提供給我們的 block,我們有時(shí)候自己寫(xiě)的方法里也許也想要用到 block振惰。我們來(lái)看一些簡(jiǎn)單的示例代碼歌溉,這段代碼聲明了一個(gè)接收 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;
    }];
}

因?yàn)?block 就是一個(gè) Objective-C 對(duì)象,所以我們可以把 block 存儲(chǔ)在一個(gè) property 中以便之后調(diào)用骑晶。這種方式在處理異步任務(wù)的時(shí)候特別有用痛垛,我們可以在一個(gè)異步任務(wù)完成之后存儲(chǔ)一個(gè) block,之后可以調(diào)用桶蛔。下面是一段示例代碼:

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

// 存儲(chǔ) block 以便之后調(diào)用
-(void)doMathWithBlock:(int (^)(int, int))mathBlock {
    self.mathBlock = mathBlock;
}
 
// 調(diào)用上面的方法匙头,并傳入一個(gè) 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 來(lái)簡(jiǎn)化block 語(yǔ)法仔雷,當(dāng)然效果和上面的是差不多的蹂析,我們來(lái)看下面的例子:

typedef int (^MathBlock)(int, int);
 
// 使用 tpyedef 聲明一個(gè)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)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市碟婆,隨后出現(xiàn)的幾起案子电抚,更是在濱河造成了極大的恐慌,老刑警劉巖脑融,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件喻频,死亡現(xiàn)場(chǎng)離奇詭異缩宜,居然都是意外死亡肘迎,警方通過(guò)查閱死者的電腦和手機(jī)甥温,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)妓布,“玉大人姻蚓,你說(shuō)我怎么就攤上這事∠徽樱” “怎么了狰挡?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)释涛。 經(jīng)常有香客問(wèn)我加叁,道長(zhǎng),這世上最難降的妖魔是什么唇撬? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任它匕,我火速辦了婚禮,結(jié)果婚禮上窖认,老公的妹妹穿的比我還像新娘豫柬。我一直安慰自己,他們只是感情好扑浸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布烧给。 她就那樣靜靜地躺著,像睡著了一般喝噪。 火紅的嫁衣襯著肌膚如雪础嫡。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天仙逻,我揣著相機(jī)與錄音驰吓,去河邊找鬼。 笑死系奉,一個(gè)胖子當(dāng)著我的面吹牛檬贰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播缺亮,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼翁涤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了萌踱?” 一聲冷哼從身側(cè)響起葵礼,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎并鸵,沒(méi)想到半個(gè)月后鸳粉,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡园担,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年届谈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了枯夜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡艰山,死狀恐怖湖雹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情曙搬,我是刑警寧澤摔吏,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站纵装,受9級(jí)特大地震影響征讲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜橡娄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一稳诚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瀑踢,春花似錦扳还、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至棘劣,卻和暖如春俏让,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背茬暇。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工首昔, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人糙俗。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓勒奇,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親巧骚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赊颠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

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