iOS - Block用法

前言

1、理解
Block其實(shí)就是一個(gè)代碼塊。本質(zhì)上來(lái)說(shuō),一個(gè)Block就是一段能夠在將來(lái)被執(zhí)行的代碼碴巾。然而Block又是一個(gè)普通的Objective-C對(duì)象,正因?yàn)樗菍?duì)象,Block可以被作為參數(shù)傳遞,可以作為返回值從一個(gè)方法返回,可以用來(lái)給變量賦值丑搔。
2厦瓢、特點(diǎn)
將代碼放在Block中,使代碼更簡(jiǎn)潔緊湊,易于閱讀, 而且比函數(shù)使用更方便、更美觀啤月。Block其實(shí)是對(duì)閉包的實(shí)現(xiàn)煮仇。
3、Block的優(yōu)勢(shì)
在Block之前,如果我們想要調(diào)用一段代碼,然后之后一段時(shí)間,讓它給我們返回,我們一般會(huì)使用delegate或者NSNotification谎仲。但是使用過(guò) delegate 和 NSNotification 大家就應(yīng)該會(huì)感覺(jué)到——我們會(huì)不可避免的將代碼寫的到處都是,我們需要在某處開始一個(gè)任務(wù),在另外一個(gè)地方來(lái)處理這個(gè)返回結(jié)果浙垫。使用 Block 就可以在一定程度上避免這個(gè)問(wèn)題。

下面進(jìn)入主題

Block是什么

Block : 帶有自動(dòng)變量(局部變量)的匿名函數(shù)

匿名函數(shù) :沒(méi)有函數(shù)名的函數(shù)郑诺,一對(duì){}包裹的內(nèi)容是匿名函數(shù)的作用域
自動(dòng)變量:棧上聲明的一個(gè)變量不是靜態(tài)變量和全局變量夹姥,是不可以在這個(gè)棧內(nèi)聲明的匿名函數(shù)中使用的,但在Block中卻可以辙诞。

關(guān)于帶有自動(dòng)變量的含義辙售,這是因?yàn)锽lock有捕獲外部變量的功能。能夠保存外部變量的瞬間值飞涂,所以即便在block外修改變量的值旦部,也不會(huì)對(duì)Block截獲的自動(dòng)變量的值產(chǎn)生影響。

例 一道比較經(jīng)典的面試題:

int val = 10;
void (^blk)(void) = ^{ 
    printf("val=%d\n",val);
}; 
val = 2; 
blk(); 

我們都知道這段代碼 輸出val的值為 10 而不是2较店,這是因?yàn)锽lock截獲變量并保存變量的瞬間值

Block 語(yǔ)法

  1. Block的聲明及定義
//返回值(^Blok名字)(參數(shù)列表) = ^返回值(參數(shù)列表) {實(shí)現(xiàn)};
    //標(biāo)準(zhǔn)的定義和聲明
    int(^blk)(int count) = ^int(int count) {
        return count++;
    };
    blk(1);
    //當(dāng)返回值為void  實(shí)現(xiàn)部分可以忽略void不寫 即 ^(int count){}
    void(^blk1)(int count) = ^void(int count) {
        count++;
    };
    blk1(1);
    //當(dāng)參數(shù)為void 實(shí)現(xiàn)部分可以忽略參數(shù)不寫 即 ^int{}
    int(^blk2)(void) = ^int(void) {
        return 1;
    };
    blk2();
    //當(dāng)參數(shù)和返回值都為void 實(shí)現(xiàn)部分可以簡(jiǎn)寫為 ^{}
    void(^blk3)(void) = ^void(void) {
        NSLog(@"參數(shù)和返回值都為void");
    };
    blk3();
    //匿名Block 只有實(shí)現(xiàn)部分 沒(méi)有函數(shù)名
//    ^int(int count) {
//        return count;
//    };
  1. typedef簡(jiǎn)化Block的聲明
//typedef 定義block  返回值(^Blok名字)(參數(shù)列表)
typedef void(^blk)(void); //無(wú)返回值  無(wú)參數(shù)
typedef void(^blk1)(NSString *name);//無(wú)返回值  有參數(shù)
typedef int(^blk2)(void);//有返回值 無(wú)參數(shù)
typedef int(^blk3)(int count);//有返回值 有參數(shù)

Block類型

根據(jù)Block在內(nèi)存中存儲(chǔ)的位置分為三種類型:

  • NSGlobalBlock是位于全局區(qū)的block
  • NSMallocBlock是位于堆區(qū)的block
  • NSStackBlock是位于棧區(qū)的block
    //全局block NSGlobalBlock
    void(^blk)(void) = ^{
        NSLog(@"blk");
    };
    blk();
    NSLog(@" -- %@",blk);
    
    void(^blk3)(int count) = ^(int count) {
        NSLog(@"%d",count);
    };
    blk3(20);
    NSLog(@" -- %@",blk3);
    NSLog(@"-----------------------------------------");
    
    //堆block NSMallockBlock
    int a = 10;
    void(^blk1)(void) = ^{
        NSLog(@"a = %d",a);
    };
    blk1();
    NSLog(@" -- %@",blk1);
    
    __block int b = 30;
    void(^blk4)(void) = ^{
        b = 40;
        NSLog(@"%d",b);
    };
    blk4();
    NSLog(@" -- %@",blk4);
    NSLog(@"-----------------------------------------");
    
    //棧block  NSStackBlock  當(dāng)不捕獲變量a時(shí) 該block為全局block
    NSLog(@" -- %@", [^{NSLog(@"Stack Block:%d",a);} class]);

Block 用法 (傳遞數(shù)據(jù) 傳遞事件)

  1. 作為屬性
@interface ViewController ()
@property (copy, nonatomic) void(^blkCall)(NSInteger index);
@end
  1. 作為參數(shù)
@interface ViewController : UIViewController
- (void)viewController:(UIViewController *)vc callBack:(void(^)(NSString *name)) callBack;
@end
  1. 作為返回值
- (void(^)(int count))func {
    return ^(int count) {
        NSLog(@"返回");
    };
}

作為返回值的情況我沒(méi)怎么用過(guò)士八,前兩種用法詳見(jiàn) demo

Block 內(nèi)存問(wèn)題

在Block中某個(gè)對(duì)象持有Block本身,而Block又持有該對(duì)象就會(huì)引起內(nèi)存泄漏(通常是引用self)泽西,這里介紹幾種解決循環(huán)引用的方法

  1. 我們最常用的 使用__weak typeOf(self)
__weak typeof(self) weakSelf = self;
self.blk = ^{
    NSLog(@"In Block : %@",weakSelf);
 };
  1. 使用 __block ClassName
__block XXViewController* blockSelf = self;
self.blk = ^{
    NSLog(@"In Block : %@",blockSelf);
    blkSelf = nil;//不能省略
 };
self.blk();//該block必須執(zhí)行一次曹铃,否則還是內(nèi)存泄露

使用該方法解決內(nèi)存問(wèn)題,一定要注意在block代碼塊內(nèi)捧杉,使用完使用完__block變量后將其設(shè)為nil陕见,并且該block必須至少執(zhí)行一次后秘血,不存在內(nèi)存泄露

  1. 將在Block內(nèi)要使用到的對(duì)象(一般為self對(duì)象),以Block參數(shù)的形式傳入评甜,Block就不會(huì)捕獲該對(duì)象灰粮,而將其作為參數(shù)使用,其生命周期系統(tǒng)的棧自動(dòng)管理忍坷,不造成內(nèi)存泄露粘舟。
   self.blk = ^(UIViewController *vc) {
        NSLog(@"Use Property:%@", vc.name);
    };
    self.blk(self);

附: Block內(nèi)修改外部變量的值 __block修飾符

 __block int a = 0;
 void (^foo)(void) = ^{
     a = 1;
 };
 foo(); //這里,a的值被修改為1

__block保證了棧上和Block內(nèi)(通常在堆上)可以訪問(wèn)和修改“同一個(gè)變量”佩研,__block是如何實(shí)現(xiàn)這一功能的柑肴?

__block發(fā)揮作用的原理:將棧上用__block修飾的自動(dòng)變量封裝成一個(gè)結(jié)構(gòu)體,讓其在堆上創(chuàng)建旬薯,以方便從棧上或堆上訪問(wèn)和修改同一份數(shù)據(jù)晰骑。

這里沒(méi)有探究Block底層的實(shí)現(xiàn),如果有研究底層很厲害的大神請(qǐng)賜教绊序,另外有錯(cuò)誤的或者遺漏的地方硕舆,請(qǐng)指教!V韫抚官!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市阶捆,隨后出現(xiàn)的幾起案子凌节,更是在濱河造成了極大的恐慌,老刑警劉巖趁猴,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件刊咳,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡儡司,警方通過(guò)查閱死者的電腦和手機(jī)娱挨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)捕犬,“玉大人跷坝,你說(shuō)我怎么就攤上這事〉锏铮” “怎么了柴钻?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)垢粮。 經(jīng)常有香客問(wèn)我贴届,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任毫蚓,我火速辦了婚禮占键,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘元潘。我一直安慰自己畔乙,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布翩概。 她就那樣靜靜地躺著牲距,像睡著了一般。 火紅的嫁衣襯著肌膚如雪钥庇。 梳的紋絲不亂的頭發(fā)上牍鞠,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音上沐,去河邊找鬼皮服。 笑死,一個(gè)胖子當(dāng)著我的面吹牛参咙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播硫眯,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼蕴侧,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了两入?” 一聲冷哼從身側(cè)響起净宵,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎裹纳,沒(méi)想到半個(gè)月后择葡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡剃氧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年敏储,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朋鞍。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡已添,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出滥酥,到底是詐尸還是另有隱情更舞,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布坎吻,位于F島的核電站缆蝉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜刊头,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一贝搁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧芽偏,春花似錦雷逆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至被碗,卻和暖如春某宪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背锐朴。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工兴喂, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人焚志。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓衣迷,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親酱酬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子壶谒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

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

  • 《目標(biāo)汗菜,下一站》 文/鐵牟 遠(yuǎn)方,一條游馳的巨蟒 一路翻山越嶺的奔波挑社,呼哧 你看它慢下來(lái)了 慢下來(lái)陨界,等著啜息 穿著...
    鐵牟閱讀 232評(píng)論 0 1
  • 自從上段感情結(jié)束,我一直都沒(méi)有承認(rèn)痛阻,他到底帶給我了什么菌瘪?我一直覺(jué)得他給我的是焦慮,除了我覺(jué)得他不可靠外录平,我也沒(méi)覺(jué)得...
    尹莉莎閱讀 610評(píng)論 0 1
  • ?隨著企業(yè)規(guī)模的不斷擴(kuò)大麻车,員工的不斷增多,漸漸對(duì)管理人員的執(zhí)行力提出了新的要求斗这。的確动猬,沒(méi)有執(zhí)行,浮在腦海里的藍(lán)圖永...
    163誠(chéng)信品牌講師閱讀 300評(píng)論 0 1
  • 我自屋頂表箭、飛奔赁咙、落下 劃出弧度、伸展、肆意狂灑 你自天邊彼水、游走崔拥、滑翔 飄忽不定、蜷縮凤覆、慵懶疲乏 我驅(qū)夜而入链瓦、漫濕空...
    ___零點(diǎn)閱讀 529評(píng)論 2 8