Block用法總結(jié)

一读处、 block語法格式,如下

//return type (^BlockName)(list of arguments) = ^return type (list of arguments){do something;};

根據(jù)上面的英文應(yīng)該就能理解各部分是什么了,接下來是一個簡單的例子丹锹,可以幫助理解扒接,這里引用了 ?Sindri的小巢(簡書作者)的文章

原文鏈接:http://www.reibang.com/p/29d70274374b

int (^sumOfNumbers) (int a, int b) = ^int(int a, int b) {

return a + b;

};

//Block的調(diào)用

int result = sumOfNumbers(100, 200); //無參時,也要帶上小括號

NSLog(@"result = %d", result);

1.等號左側(cè)聲明一個名為sumOfNumbers的代碼塊精钮,名稱前用^符號表示后面的字符串是block的名稱;

2.最左側(cè)的int表示這個block的返回值乞娄;

3.括號中間表示這個block的參數(shù)列表瞬逊,這里接收兩個int類型的參數(shù)显歧。

4. 而在等號右側(cè)表示這個block的實現(xiàn),其中返回值類型是可以省略的确镊,編譯器會根據(jù)上下文自動補充返回值類型士骤。

5.使用^符號銜接著一個參數(shù)列表,使用括號包起來蕾域,告訴編譯器這是一個block拷肌,然后使用大括號將block的代碼封裝起來。

二旨巷、 block的直接使用(匿名Block對象)

NSArray *array = @[@"3", @"1", @"2"];

array = [array sortedArrayUsingComparator:^NSComparisonResult(id ?_Nonnull obj1, id ?_Nonnull obj2) {

return[obj1 compare: obj2];

}];

NSLog(@"array = %@", array);

其實理解起來也很簡單巨缘,匿名的Block對象/是可以傳遞給/方法的Block對象/的,而不需要先賦值給變量契沫。

讓我們先看看匿名的整數(shù)。有三種方法可以將整數(shù)傳遞給方法:

方法1: 聲明昔汉、賦值和使用完全分開

int i;

i = 5;

NSNumber *num = [NSNumber numberWithInt:i];

方法2: 在一行中聲明賦值使用

int i = 5;

NSNumber *num = [NSNumber numberWithInt:i];

方法3:跳過變量聲明步驟

NSNumber *num = [NSNumber numberWithInt:5];

如果采用第三種方法懈万,就是匿名地傳遞一個整數(shù)。因為它沒有名字靶病,所以說它是匿名的会通。

而將Block對象傳遞給方法的辦法和傳遞整數(shù)相同。分別用三行代碼來聲明Block對象娄周,然后賦值涕侈,最后使用。但是匿名傳遞Block對象更加常用煤辨。

三裳涛、類型重定義(block重命名)

Block對象的語法可能會比較復(fù)雜。通過使用第11章介紹過的typedef關(guān)鍵字众辨,可以將某個Block對象類型定義為一個新類型端三,以方便使用。需要注意的是鹃彻,不能在方法的實現(xiàn)代碼中使用typedef郊闯。也就是說,應(yīng)該在實現(xiàn)文件的頂部蛛株,或者頭文件內(nèi)使用typedef团赁。在main.m中,添加以下代碼:

#import

typedef int (^SumBlock)(int a, int b);

int main (int argc, const char * argv[])

{

這段代碼中的typedef語句看上去與Block變量聲明很像谨履,但是欢摄,這里定義的是一個新的類型,而不是變量笋粟。跟在^字符后面的是類型名稱剧浸。創(chuàng)建這個新類型后锹引,就能簡化相應(yīng)Block對象的聲明。

現(xiàn)在使用新的類型聲明SumBlock:

注意唆香,這里的Block類型只是聲明了Block對象的實參和返回類型嫌变,并沒有實現(xiàn)真正的Block對象。

SumBlock sumBlock = ^(int a, int b){

return a + b;

};

int result = sumBlock(100, 200);

NSLog(@"result = %d", result);

四躬它、外部變量 ——?block對棧區(qū)變量做只讀拷貝操作,使用的是對變量的copy,而不是變量本身

Block對象通常會(在其代碼中)使用外部創(chuàng)建的其他變量(基本類型的變量腾啥,或者是指向其他對象的指針)。這些外部創(chuàng)建的變量叫做外部變量(external variables)冯吓。當(dāng)執(zhí)行Block對象時倘待,為了確保其下的外部變量能夠始終存在,相應(yīng)的Block對象會捕獲(captured)這些變量组贺。對基本類型的變量凸舵,捕獲意味著程序會拷貝變量的值,并用Block對象內(nèi)的局部變量保存失尖。對指針類型的變量啊奄,Block對象會使用強引用。這意味著凡是Block對象用到的對象掀潮,都會被保留菇夸。所以在相應(yīng)的Block對象被釋放前,這些對象一定不會被釋放(這也是Block對象和函數(shù)之間的差別仪吧,函數(shù)無法做到這點)庄新。

使用外部變量

int a = 200, b = 100;

int (^minusBlock) (void) = ^(void){return a - b;};

NSLog(@"minus1 = %d", minusBlock());

//結(jié)果為100

a = 500, b = 200;

NSLog(@"minus2 = %d", minusBlock());

//結(jié)果還是100

只需要在定義外部變量的時候,使用 __block 或者 static 修飾薯鼠。

__block int a = 200, b = 100;//或者 static

int (^minusBlock) (void) = ^(void){return a - b;};

NSLog(@"minus1 = %d", minusBlock());

//結(jié)果為100

a = 500, b = 200;

NSLog(@"minus2 = %d", minusBlock());

//結(jié)果是300

修改外部變量

在Block對象中择诈,被捕獲的變量是常數(shù),程序無法修改變量所保存的值出皇。如果需要在Block對象內(nèi)修改某個外部變量吭从,則可以在聲明相應(yīng)的外部變量時,在前面加上__block關(guān)鍵字恶迈,否則涩金,如果在block內(nèi)部改變外部變量的值程序就會報錯

這樣寫是沒問題的

void(^oneBlock)(void) = ^(void){

int n = 1;

n = 2;

NSLog(@"n = %d", n);

};

oneBlock();//結(jié)果是2

下面的寫法會報錯

int n = 1;

//定義Block

void(^oneBlock)(void) = ^(void){

n = 2;//報錯!!!!!!

NSLog(@"n = %d", n);

};

oneBlock();

同樣可以使用__block修飾外部變量來解決這個問題(使用static修飾或定義成實例變量的方法解決)

__block int n = 1;

//定義Block

void(^oneBlock)(void) = ^(void){

n = 2;

NSLog(@"n = %d", n);

};

oneBlock();//結(jié)果是2

在block對象中使用self

如果需要寫一個使用self的Block對象,就必須要多做幾步工作來避免循環(huán)引用問題暇仲。下面舉個簡單的例子

self.nameLabel.text = @"xiaoming";

void(^strBlock)(void) = ^(void){

NSString*name = self.nameLabel.text;

NSLog(@"%@", name);

};

strBlock();

Block中使用了self步做,這個Block對象會捕獲self,Block必須等到所引用的對象全部釋放后才會釋放奈附,然而全度,self又要到程序運行結(jié)束才釋放,這樣Block就不可能得到釋放斥滤,就陷入強引用循環(huán)了将鸵。

為了打破這個強引用循環(huán)勉盅,可以先在Block對象外聲明一個__block指針(ARC下使用__weak),然后將這個指針指向Block對象使用的self顶掉,最后在Block對象中使用這個新的指針:

__weak RootViewController *weakSelf = self; // 一個弱引用指針

void(^strBlock)(void) = ^(void){

NSString*name =weakSelf.nameLabel.text;

NSLog(@"%@", name);

};

如果想要了解的更深入草娜,可以繼續(xù)看完下面的內(nèi)容,舉例有所變化痒筒,但應(yīng)該可以理解宰闰。

=======================================

現(xiàn)在這個Block對象對BNREMployee實例是弱引用,強引用循環(huán)打破了簿透。

然而移袍,由于是弱引用,所以self指向的對象在Block執(zhí)行的時候可能會被釋放老充。

為了避免這種情況的發(fā)生葡盗,可以在Block對象中創(chuàng)建一個對self的局部強引用:

__weak BNREmployee *weakSelf = self; // 弱引用

myBlock = ^{

BNREmployee *innerSelf = weakSelf; // 局部強引用

NSLog(@"Employee: %@", innerSelf);

};

通過創(chuàng)建innerSelf強引用,就可以在Block和BNREmployee實例中再次創(chuàng)建一個強引用循環(huán)啡浊。但是觅够,由于innerSelf引用是針對Block內(nèi)部的,所以只有在Block執(zhí)行的時候它才會執(zhí)行虫啥,而Block結(jié)束之后就會自動消失蔚约。

每次寫B(tài)lock對象的時候都引用self會是一個很好的練習(xí)奄妨。

在Block對象中無意使用self涂籽,而是使用了實例變量的情況

如果直接在Block對象中使用實例變量,那么block會捕獲self砸抛,而不會捕獲實例變量评雌。這是實例變量的一個鮮為人知的特點。例如直焙,以下這段代碼直接存取一個實例變量:

__weak BNREmployee *weakSelf = self;

myBlock = ^{

BNREmployee *innerSelf = weakSelf; // 局部強引用

NSLog(@"Employee: %@", innerSelf);

NSLog(@"Employee ID: %d", _employeeID);

};

編譯器是這么解讀這段代碼的:

__weak BNREmployee *weakSelf = self;

myBlock = ^{

BNREmployee *innerSelf = weakSelf; // 局部強引用

NSLog(@"Employee: %@", innerSelf);

NSLog(@"Employee ID: %d", self->_employeeID);

};

->語法看上去是不是很熟悉景东?這個語法實際是用來后去堆上的成員結(jié)構(gòu)的。從最底層來說奔誓,對象實際就是結(jié)構(gòu)斤吐。

由于編譯器將_employeeID看成是self->_employeeID,self就被Block對象無意地捕獲了厨喂。這樣又會造成之前使用weakSelf和innerSelf避免的強引用循環(huán)和措。

怎樣解決呢?不要直接存取實例變量蜕煌。使用存取方法派阱!

__weak BNREmployee *weakSelf = self;

myBlock = ^{

BNREmployee *innerSelf = weakSelf; // 局部強引用

NSLog(@"Employee: %@", innerSelf);

NSLog(@"Employee ID: %d", innerSelf.employeeID);

};

現(xiàn)在沒有直接地使用self了,就不會造成無意識地強引用循環(huán)斜纪。

在這種情況下贫母,重要的是要理解編譯器是如何思考的文兑,這樣才能避免隱藏的強引用循環(huán)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末腺劣,一起剝皮案震驚了整個濱河市绿贞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌誓酒,老刑警劉巖樟蠕,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異靠柑,居然都是意外死亡寨辩,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進(jìn)店門歼冰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來靡狞,“玉大人,你說我怎么就攤上這事隔嫡〉榕拢” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵腮恩,是天一觀的道長梢杭。 經(jīng)常有香客問我,道長秸滴,這世上最難降的妖魔是什么武契? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮荡含,結(jié)果婚禮上咒唆,老公的妹妹穿的比我還像新娘。我一直安慰自己释液,他們只是感情好全释,可當(dāng)我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著误债,像睡著了一般浸船。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上寝蹈,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天李命,我揣著相機與錄音,去河邊找鬼躺盛。 笑死项戴,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的槽惫。 我是一名探鬼主播周叮,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼辩撑,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了仿耽?” 一聲冷哼從身側(cè)響起合冀,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎项贺,沒想到半個月后君躺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡开缎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年棕叫,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奕删。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡俺泣,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出完残,到底是詐尸還是另有隱情伏钠,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布谨设,位于F島的核電站熟掂,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏扎拣。R本人自食惡果不足惜赴肚,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鹏秋。 院中可真熱鬧尊蚁,春花似錦亡笑、人聲如沸侣夷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽百拓。三九已至,卻和暖如春晰甚,著一層夾襖步出監(jiān)牢的瞬間衙传,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工厕九, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蓖捶,地道東北人。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓扁远,卻偏偏與公主長得像俊鱼,于是被迫代替她去往敵國和親刻像。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,515評論 2 359