Block 的使用

**Block **是iOS在4.0之后新增的語(yǔ)法蛹磺,在iOS SDK 4.0之后,block幾乎出現(xiàn)在所有新版的API之中,換句話說(shuō)笛谦,如果不了解block這個(gè)概念就無(wú)法使用SDK 4.0版本以后的新功能碗暗,所以我們有必要去學(xué)習(xí)下block的使用了颈将,英文比較好的童鞋可以自行參照官方文檔,本文中的示例采摘于官方文檔.

  1. Block的定義
    在這一小節(jié)我們先用一些簡(jiǎn)單范例來(lái)引入block的概念言疗。

1.1 聲明和創(chuàng)建Block:
 我們使用「^」運(yùn)算子來(lái)聲明一個(gè)block晴圾,而且在block的定義最后面要加上「;」來(lái)表示一個(gè)完整的述句,下面是一個(gè)block的范例:

int multiplier = 7;
int (^myBlock)(int) = ^(int num){
          return num * multiplier;
};

例子中我們聲明了一個(gè)myBlock的代碼塊噪奄,用「^」符號(hào)來(lái)表示這是一個(gè)block死姚。聲明告訴我們myBlock是一個(gè)入?yún)⒑统鰠⒍紴檎停╥nt)的block。

值得注意的地方是block可以使用和本身定義范圍相同的變數(shù)勤篮,在上面的例子中multiplier 和myBlock 都是某一個(gè)函數(shù)內(nèi)定義的兩個(gè)變量都毒,都在某個(gè)函數(shù)兩個(gè)大括號(hào)「{」和「 }」中間的區(qū)塊,因此它們的有效范圍是相同的碰缔,所以在block中就可以直接使用 multiplier 這個(gè)變數(shù)账劲,此外當(dāng)把block定義成一個(gè)變量的時(shí),我們可以直接像使用一般函數(shù)般的方式使用它:

int multiplier = 7 ;
int (^myBlock)( int ) = ^( int num){
 return num * multiplier;
};
printf ( "%d" , myBlock( 3 ));
//結(jié)果會(huì)打印出21

1.2 直接使用Block
 在很多情況下,我們并不需要將block聲明為變量涤垫,反之我們可以直接在需要使用block的地方直接用內(nèi)嵌的方式將block的內(nèi)容寫出來(lái)姑尺,在下面的例子中AFNet的一個(gè)函數(shù),就是直接使用block做為它的參數(shù):

- (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request
                                                    success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
                                                    failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
}

1.3 __block 變量
  一般來(lái)說(shuō)蝠猬,在block內(nèi)只能讀取在同一個(gè)作用域的變量而且沒(méi)有辦法修改在block外定義的任何變量切蟋,如果我們想要這些變量能夠在block中被修改,就必須在前面掛上__block的修飾詞榆芦,以上面第一個(gè)例子中的 multiplier 來(lái)說(shuō)柄粹,這個(gè)變數(shù)在 block 中是只讀的,所以 multiplier = 7 指定完后匆绣,在 block 中的 multiplier 就只能是 7 不能修改驻右,若我們?cè)?block 中修改 multiplier,在編譯時(shí)就會(huì)報(bào)錯(cuò)崎淳,因此若要在 block 中修改 multiplier 堪夭,就必須在 multiplier 前面加上__block的修飾詞,請(qǐng)參考下面的范例:

__block int multiplier = 7;
     int (^myBlock)(int) = ^(int num){ 
                if (num > 5 ) {
                multiplier = 7 ;
                }
                else {
                multiplier = 10 ; 
                } 
                return num * multiplier; 
  };
printf ( "%d" , myBlock( 3 ));
//結(jié)果會(huì)打印出30
  1. Block概要
     Block 提供我們一種能夠?qū)⒑瘮?shù)程式碼內(nèi)嵌在一般述句中的方法拣凹,在其他語(yǔ)言中也有類似的概念稱做「closure」森爽,也就是通常我們說(shuō)的閉包的概念。

2.1 Block 的功能
 Block 是一種具有匿名功能的內(nèi)嵌函數(shù)嚣镜,它的特性如下: 如一般的函數(shù)般能擁有帶有型態(tài)的參數(shù)爬迟。擁有回傳值【漳洌可以擷取被定義的詞法作用域(lexical scope)狀態(tài)付呕。可以選擇性地修改詞法作用域的狀態(tài)跌捆。
注:詞法作用域(lexical scope)可以想像成是某個(gè)函數(shù)兩個(gè)大括號(hào)中間的區(qū)塊徽职,這個(gè)區(qū)塊在程式執(zhí)行時(shí),系統(tǒng)會(huì)將這個(gè)區(qū)塊放入堆疊記憶體中佩厚,在這個(gè)區(qū)塊中的宣告的變數(shù)就像是我們常聽到的區(qū)域變數(shù)姆钉,當(dāng)我們說(shuō)block可以擷取同一詞法作用域的狀態(tài)時(shí)可以想像block變數(shù)和其他區(qū)域變數(shù)是同一個(gè)層級(jí)的區(qū)域變數(shù)(位于同一層的堆疊里),而block的內(nèi)容可以讀取到和他同一層級(jí)的其他區(qū)域變數(shù)可款。我們可以拷貝一個(gè)block育韩,也可以將它丟到其他的執(zhí)行緒中使用,基本上雖然block在iOS程式開發(fā)中可以使用在C/C++開發(fā)的程式片段闺鲸,也可以在Objective-C中使用筋讨,不過(guò)在系統(tǒng)的定義上,block永遠(yuǎn)會(huì)被視為是一個(gè)Objective-C的物件摸恍。
2.2 Block 的使用時(shí)機(jī)
 Block 一般是用來(lái)表示悉罕、簡(jiǎn)化一小段的程式碼赤屋,它特別適合用來(lái)建立一些同步執(zhí)行的程式片段、封裝一些小型的工作或是用來(lái)做為某一個(gè)工作完成時(shí)的回傳呼叫(callback) 壁袄。在新的iOS API中block被大量用來(lái)取代傳統(tǒng)的delegate和callback类早,而新的API會(huì)大量使用block主要是基于以下兩個(gè)原因:
一、可以直接在程式碼中撰寫等會(huì)要接著執(zhí)行的程式嗜逻,直接將程式碼變成函數(shù)的參數(shù)傳入函數(shù)中涩僻,這是新API最常使用block的地方。二栈顷、可以存取區(qū)域變數(shù)逆日,在傳統(tǒng)的callback實(shí)作時(shí),若想要存取區(qū)域變數(shù)得將變數(shù)封裝成結(jié)構(gòu)才能使用萄凤,而block則是可以很方便地直接存取區(qū)域變數(shù)室抽。

  1. 聲明和創(chuàng)建Block
    3.1 聲明一個(gè)Block變量的參考
// 入?yún)榭?出數(shù)也為空的block
void(^blockReturnVoidWithVoidArgument)(void);
// 出參為整型(int),兩個(gè)入?yún)⒎謩e是整型(int)和字符類型(char)的block
int(^blockReturnIntWithIntAndCharArguments)(int,char);
//出參為空靡努,含有10個(gè)block的數(shù)組坪圾,每個(gè)block都有一個(gè)類型為int的入?yún)?void(^arrayOfTenBlocksReturnVoidWinIntArgument[10])(int);

3.2 創(chuàng)建一個(gè)Block
 我們使用「^」聲明一個(gè)block,并在最后使用「;」來(lái)表示結(jié)束惑朦,下面的范例示范了一個(gè)block變量兽泄,然后再定義一個(gè)block把它指定給block變量:

/* 聲明block 變量*/
int (^oneBlock)(int);
/* 定義一個(gè)block 并指定給上面聲明的變量*/
oneBlock = ^(int anInt)
{ 
        return anInt = - 1 ; 
};

3.3 全局的Block
 聲明一個(gè)全局的block行嗤,請(qǐng)參考以下范例:

  int GlobalInt = 0;
  int(^getGlobalInt)(void) = ^(void){
         return GlobalInt;
 }
  1. Block和變量
    4.1 變量的形態(tài)
    我們可以在block中遇到平常在函數(shù)中會(huì)遇到的變量類型:
        全域(global)變量或是靜態(tài)的區(qū)域變量(static local)已日。
         全域的函數(shù)垛耳。
         區(qū)域變量和由封閉領(lǐng)域(enclosing scope)傳入的參數(shù)栅屏。
         除了上述之外block額外支援了另外兩種變量:在函數(shù)內(nèi)可以使用**__block** 變量,這些變量在block中是可被修改的堂鲜。匯入常數(shù)(const imports)栈雳。
         此外,在方法的實(shí)作里缔莲,block可以使用Objective-C的實(shí)體變量(instance variable)哥纫。
   下列的規(guī)則可以套用到在block中變量的使用:
        可以存取全域變量和在同一領(lǐng)域(enclosing lexical scope)中的靜態(tài)變量。
        可以存取傳入block的參數(shù)(使用方式和傳入函數(shù)的參數(shù)相同)痴奏。
        在同一領(lǐng)域的區(qū)域變數(shù)在block中將視為常數(shù)(const)蛀骇。
        可以存取在同一領(lǐng)域中以__block 為修飾詞的變數(shù)。
        在block中宣告的區(qū)域變數(shù)读拆,使用方式和平常函數(shù)使用區(qū)域變數(shù)的方式相同擅憔。
        下面的例子介紹了區(qū)域變數(shù)(上述第三點(diǎn))的使用方式:
 int x = 123;
 void (^printXandY)(int) = ^(int y)
 {
     printf("%d##%d",x,y);
 } 
 //printXAndY(456);將會(huì)輸出123##456

4.2 __block 型態(tài)變量
 我們可以藉由將一個(gè)由外部匯入block的變量放上修飾詞__block來(lái)讓這個(gè)變數(shù)由只讀變成讀寫,不過(guò)有一個(gè)限制就是傳入的變量在記憶體中必須是一個(gè)占有固定長(zhǎng)度記憶體的變數(shù)__block修飾詞無(wú)法使用于像是變動(dòng)長(zhǎng)度的陣列這類不定長(zhǎng)度的變數(shù)檐晕,請(qǐng)參考下面的范例:

//加上__block 修飾詞暑诸,所以可以在block 中被修改蚌讼。
__block int x = 123;
void (^printXandY)(int) = ^(int y){
          x = x+y;
          printf("%d %d",x,y);
}
printXAndY( 456 ); // 將會(huì)印出579 456

下面我們使用一個(gè)范例來(lái)介紹各類型的變數(shù)和block之間的互動(dòng):

extern NSInteger CounterGlobal;
static NSInteger CounterStatic;
{ 
          NSInteger localCounter = 42 ; 
          __block char localCharacter;
         void(^aBlock)(void) = ^(void){
         ++ CounterGlobal; //可以存取 
         ++ CounterStatic; //可以存取
         CounterGlobal = localCounter; //localCounter在block 建立時(shí)就不可變了
         localCharacter = 'a' ; //設(shè)定外面定義的localCharacter 變量
         }
         ++localCounter; //不會(huì)影響的block 中的值
         localCharacter = 'b';
         aBlock(); //執(zhí)行block 的內(nèi)容
         //執(zhí)行完后,localCharachter 會(huì)變成'a'
}

4.3 block 中的引用計(jì)數(shù)問(wèn)題
我們?cè)赽lock中引用外部的變量个榕,在一般的情況下它將會(huì)自動(dòng)增加變量的引用計(jì)數(shù)篡石,不過(guò)若以__block作為修飾,引用計(jì)數(shù)不受影響西采,因此我們需要注意兩點(diǎn)
 』巳1. 若直接引用實(shí)例變量(instance variable),self的引用計(jì)數(shù)加1械馆。
 」得铩2.若通過(guò)變量存取實(shí)例變數(shù)的值,只是實(shí)例變量的引用計(jì)數(shù)加1狱杰。
   以下范例說(shuō)明上面兩種情況瘦材,假設(shè)instanceVariable是實(shí)體變量:

dispath_async(queue,^{
  //因?yàn)橹苯哟嫒?shí)體變量instanceVariable ,所以self 的retain count會(huì)加1
         doSomethingWithObject (instanceVariable);
 });
id localVaribale = instanceVariable;
dispatch_async(queue,^{
       //localVariable是存取值仿畸,所以這時(shí)只有l(wèi)ocalVariable 的retain count 加1
       //self 的return count并不會(huì)增加食棕。
       doSomethingWithObject (localVaribale);
});
  1. 使用Block
    5.1 直接使用
    我們可以像使用一般函數(shù)的方式來(lái)使用它,請(qǐng)參考下面兩個(gè)范例:
int(^aBlock)(int) = ^(int aInt){
          return aInt-1;
};
printf("10 minus 1 is %d", aBlock(10));
//結(jié)果會(huì)顯示:10 minus 1 is 9
float(^distanceTraveled)(float, float, float) = ^(float startingSpeed, float acceleration, float time){
          float distance = (startingSpeed * time) + ( 0.5 * acceleration * time * time);
          return distance;
};
float howFar = distanceTraveled( 0.0 , 9.8 , 1.0 );
//howFar的值為4.9

在一般常見的情況中错沽,若是將block當(dāng)做是參數(shù)傳入函數(shù)簿晓,我們通常會(huì)使用「內(nèi)嵌」的方式來(lái)使用block。
5.2 將Block當(dāng)作函數(shù)的參數(shù)
我們可以像一般函數(shù)使用參數(shù)的方式千埃,將block以函數(shù)參數(shù)的形式傳入函數(shù)中憔儿,在這種情況下,大多數(shù)我們使用block的方式將不會(huì)傾向聲明block而是直接以內(nèi)嵌的方式來(lái)將block傳入放可,這也是目前SDK中主流的做法:

char *myCharacters[3]={"TomJohn","George","Charles Condomine"};
qsort_b(myCharacters, 3, sizeof(char *),^(const void *l,const void *r){
           char *left = *(char **)l;
           char *right = *( char **)r;
           return strncmp (left, right, 1 );
}//這里是block 的終點(diǎn)谒臼。 
);
//最后的結(jié)果為:{"Charles Condomine", "George", "TomJohn"}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市耀里,隨后出現(xiàn)的幾起案子蜈缤,更是在濱河造成了極大的恐慌,老刑警劉巖冯挎,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件底哥,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡房官,警方通過(guò)查閱死者的電腦和手機(jī)趾徽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)翰守,“玉大人孵奶,你說(shuō)我怎么就攤上這事×拾常” “怎么了拒课?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵畦韭,是天一觀的道長(zhǎng)继效。 經(jīng)常有香客問(wèn)我寨昙,道長(zhǎng)掠归,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任卢鹦,我火速辦了婚禮臀脏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘冀自。我一直安慰自己揉稚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布熬粗。 她就那樣靜靜地躺著搀玖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪驻呐。 梳的紋絲不亂的頭發(fā)上灌诅,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音含末,去河邊找鬼猜拾。 笑死,一個(gè)胖子當(dāng)著我的面吹牛佣盒,可吹牛的內(nèi)容都是我干的挎袜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼肥惭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼盯仪!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起务豺,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤磨总,失蹤者是張志新(化名)和其女友劉穎嗦明,沒(méi)想到半個(gè)月后笼沥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡娶牌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年奔浅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片诗良。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡汹桦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鉴裹,到底是詐尸還是另有隱情舞骆,我是刑警寧澤钥弯,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站督禽,受9級(jí)特大地震影響脆霎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜狈惫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一睛蛛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧胧谈,春花似錦忆肾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至稳强,卻和暖如春郊酒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背键袱。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工燎窘, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蹄咖。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓褐健,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親澜汤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蚜迅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

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

  • Block是iOS在4.0之后新增的語(yǔ)法,在iOS SDK 4.0之后俊抵,block幾乎出現(xiàn)在所有新版的API之中谁不,...
    阿窩額咦嗚芋閱讀 409評(píng)論 0 0
  • Block是iOS在4.0之后新增的程式語(yǔ)法,嚴(yán)格來(lái)說(shuō)block的概念并不算是基礎(chǔ)程式設(shè)計(jì)的范圍徽诲,對(duì)初學(xué)者來(lái)說(shuō)也不...
    Bager閱讀 903評(píng)論 0 0
  • 1 block的基本概念 1.1 block的產(chǎn)生和用途 代碼塊Block是蘋果在iOS4開始引入的對(duì)C語(yǔ)言的擴(kuò)展...
    堂吉訶德灬閱讀 574評(píng)論 0 5
  • #Block ###Block概述 Block它是C語(yǔ)言級(jí)別和運(yùn)行時(shí)方面的一個(gè)特征刹帕。Block封裝了一段代碼邏輯,...
    是我始終拒絕成長(zhǎng)嗎閱讀 1,090評(píng)論 0 5
  • 一.圖的遍歷1.層級(jí)遍歷2.由點(diǎn)及面3.拓?fù)渑判蚨?最短路徑求簡(jiǎn)單圖最短路徑谎替,即所有邊都等權(quán)或者無(wú)權(quán)的無(wú)向圖中 能...
    6默默Welsh閱讀 352評(píng)論 0 0