關(guān)于Block塊的所有所有

關(guān)于Block:

在我們使用OC進(jìn)行iOS開(kāi)發(fā)和Mac OS開(kāi)發(fā)中,Block語(yǔ)法是我們最常見(jiàn)的語(yǔ)法之一,而且蘋(píng)果官方也推薦我們使用Block代碼塊的語(yǔ)法,如果仔細(xì)觀察,大家可以看到,在近期蘋(píng)果新開(kāi)放的系統(tǒng)框架的API中,蘋(píng)果越來(lái)越多的使用了Block這種語(yǔ)法方式,而不是@selector方法選擇器.

蘋(píng)果如此推薦Block的使用,以及這種語(yǔ)法深受廣大OC開(kāi)發(fā)者的喜愛(ài)的原因是Block不可替代的優(yōu)點(diǎn):Block的寫(xiě)法非常簡(jiǎn)潔高效,而且可以減少代碼的分散,提高內(nèi)聚性等等,雖然代理等回調(diào)方式也有它的優(yōu)點(diǎn),但是廣大OC開(kāi)發(fā)者確大都對(duì)Block情有獨(dú)鐘.

接下來(lái),我們一起來(lái)看看Block的用法,原理,以及注意點(diǎn)吧.

初識(shí)Block:

Block 是在OSX 10.4 以及iOS4 之后引入的新的語(yǔ)法形式,從技術(shù)上來(lái)講,Block應(yīng)該屬于C語(yǔ)言的特性,所以只要編譯器支持,就可以在C,C++,OC,OC++中使用.

通俗點(diǎn)來(lái)說(shuō)Block的內(nèi)容其實(shí)就是一個(gè)代碼塊,表示一段代碼.但是這個(gè)代碼塊可以有返回值,可以有參數(shù)

Block也可以看成是一個(gè)OC對(duì)象,所以它也能賦值給變量.

沒(méi)有參數(shù),沒(méi)有返回值的Block

說(shuō)一百句話(huà)也不如上一句代碼,我們先來(lái)寫(xiě)個(gè)最簡(jiǎn)單的Block吧:

^{
        NSLog(@"I am a Block");
    };
    

很簡(jiǎn)單的語(yǔ)法形式對(duì)吧,一對(duì){}前面再加上一個(gè)^,就是最簡(jiǎn)單的Block形式,這個(gè)Block只有一句代碼,沒(méi)有返回值,也沒(méi)有參數(shù).

有參數(shù),沒(méi)有返回值的Block

^(NSInteger count,NSString *str){
        NSLog(@"%ld---%@",count,str);
        
    };

這個(gè)Block帶了兩個(gè)參數(shù)分別是count和str,參數(shù)就寫(xiě)在^后面的()中就可以了

有參數(shù),有返回值的Block

^(NSInteger count,NSString *str){
        NSLog(@"%ld---%@",count,str);
        return YES;
    };

這個(gè)Block不僅有兩個(gè)參數(shù),還有返回值,返回值的類(lèi)型是一個(gè)BOOL

這些就是幾種Block的類(lèi)型啦,是不是也沒(méi)有那么復(fù)雜.

把Block賦值給變量

上面我們創(chuàng)建了三種類(lèi)型的Block,但是,我們卻無(wú)法方便的拿來(lái)使用.這個(gè)時(shí)候,我們需要把這個(gè)創(chuàng)建出來(lái)的Block賦值給一個(gè)變量:

BOOL (^aBlock)(NSInteger count,NSString *str)=^(NSInteger count,NSString *str){
        NSLog(@"%ld---%@",count,str);
        return YES;
    };

我們把上面那個(gè)既有返回值又有參數(shù)的Block賦值給了一個(gè)叫aBlock的變量,這個(gè)變量名是寫(xiě)在中間的看起來(lái)有點(diǎn)奇怪,但是熟悉了Block的語(yǔ)法就會(huì)習(xí)慣了,而BOOL (^aBlock)(NSInteger count,NSString str)表示Block的類(lèi)型.BOOL表示返回值,(NSInteger count,NSString str)表示這個(gè)Block的兩個(gè)參數(shù).

通過(guò)變量名調(diào)用Block

我們?nèi)绻枰{(diào)用這個(gè)定義的Block,則可以通過(guò)變量名如這樣:

aBlock(10,@"123");

這樣我們就調(diào)用了這個(gè)Block,并為這個(gè)Block傳入了兩個(gè)參數(shù).

內(nèi)聯(lián)Block

我們使用Block經(jīng)常是把Block當(dāng)做一個(gè)參數(shù)傳出一個(gè)方法中,這種用法叫做內(nèi)聯(lián)Block.

我們直觀的從代碼上了解這種用法就好了:

-(void)aMethodWithInlineBlock:(BOOL(^)(NSString *,NSInteger ))block
{
    if (block) {
        block(@"123",10);
    }
}

上面為一個(gè)帶Block參數(shù)的方法實(shí)現(xiàn),下面是調(diào)用這個(gè)方法的代碼:

[self aMethodWithInlineBlock:^BOOL(NSString *str,NSInteger count) {
       
        NSLog(@"%@----%ld",str,count);
        return YES;
       
    }];

Block最大的特性:捕獲變量:

在Block的諸多特性中,最顯著的最強(qiáng)大的特性是可以在Block的聲明范圍中,捕獲需要的變量.實(shí)際上,我們可以把Block看做是一個(gè)OC對(duì)象,所以它也會(huì)用引用計(jì)數(shù)來(lái)管理,如果我們?cè)贐lock中使用了某個(gè)外部變量,而該變量是對(duì)象類(lèi)型的話(huà),Block也會(huì)對(duì)這個(gè)變量有一個(gè)強(qiáng)引用,當(dāng)Block本身的引用計(jì)數(shù)變?yōu)?的時(shí)候,系統(tǒng)會(huì)釋放Block,Block也會(huì)對(duì)所捕獲的對(duì)象做一次release操作.

Block內(nèi)存結(jié)構(gòu):

類(lèi)型 變量名
void * isa
int flag
int reserved
void(*)(void) invoke
struct descriptor
捕獲到的變量 捕獲到的變量

這其中最重要的是invoke這個(gè)函數(shù)指針,它指向的是Block的實(shí)現(xiàn)代碼.

descriptor是一個(gè)結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體中有Block的大小,還有copy,dispose這兩個(gè)函數(shù)指針,分別是在拷貝和丟棄Block的時(shí)候調(diào)用的.

Block會(huì)對(duì)它捕獲到的變量指針做一個(gè)拷貝.

在棧中的Block

我們定義Block時(shí),Block所占據(jù)的其實(shí)是棧內(nèi)存空間,在棧內(nèi)存空間中,那么我們就無(wú)法控制Block的生命周期,ARC也沒(méi)有辦法對(duì)這種Block進(jìn)行引用計(jì)數(shù)的內(nèi)存管理,這種Block通常是不安全的,例如:

void (^aBlock)();
    
    if (self.condition) {
        aBlock=^{
            NSLog(@"123");
        };
    }else
    {
        aBlock=^{
            NSLog(@"456");
        };
    }
    
    aBlock();

這段代碼其實(shí)是不安全的,因?yàn)槲覀兌x出來(lái)的Block在棧內(nèi)存中,而我們不能保證在出了Block的定義區(qū)域后,這個(gè)Block還有效,那么在這種情況下,賦值給aBlock變量是不安全的,然而,我們只要對(duì)Block進(jìn)行一次copy操作,就能在堆內(nèi)存中拷貝一份Block,這樣我們就能對(duì)Block的內(nèi)存進(jìn)行控制了,ARC也能對(duì)copy出來(lái)的Block進(jìn)行內(nèi)存管理了.

上面的代碼如下改就變得安全了:

void (^aBlock)();
    
    if (self.condition) {
        aBlock=[^{
            NSLog(@"123");
        } copy];
    }else
    {
        aBlock=[^{
            NSLog(@"456");
        }copy ];
    }
    
    aBlock();

為Block變量創(chuàng)建別名

我們上面說(shuō)到Block變量名是寫(xiě)在類(lèi)型中間的,看起來(lái)似乎有點(diǎn)奇怪,而且也不是非常有利于易讀性,所以,我們一般會(huì)為Block類(lèi)型起一個(gè)別名.

之前我們對(duì)一個(gè)Block變量賦值為這種寫(xiě)法:

    BOOL (^aBlock)(NSInteger ,NSString *)=^(NSInteger count,NSString *str){
        NSLog(@"%ld---%@",count,str);
        return YES;
    };
    
    aBlock(10,@"123");

我們現(xiàn)在為這種類(lèi)型的Block起個(gè)別名

typedef BOOL (^ABlock)(NSInteger ,NSString *);

ABlock為這種Block的類(lèi)型名,我們之后再賦值給這種類(lèi)型的Block就可以如下寫(xiě)了:

ABlock aBlock=^(NSInteger count,NSString *str){
        NSLog(@"%ld---%@",count,str);
        return YES;
    };
    
    aBlock(10,@"123");

使用Block最大的注意點(diǎn):避免循環(huán)引用

我們剛剛有說(shuō)過(guò),在Block中,Block會(huì)自動(dòng)捕獲要使用到的變量,并持有該變量.這時(shí)往往容易出現(xiàn)循環(huán)引用的問(wèn)題,當(dāng)前類(lèi)實(shí)例持有該Block,該Block又在其中使用self,持有了該實(shí)例,這時(shí)循環(huán)引用問(wèn)題就出現(xiàn)了.

例如:我們?cè)诋?dāng)前類(lèi)聲明一個(gè)Block屬性:

@property (nonatomic,copy)ABlock ablock;

我們給這個(gè)Block賦值:

self.ablock=^(NSInteger count,NSString *str ){
        
        self.view.backgroundColor=[UIColor greenColor];
        
        return YES;
    };

這樣,由于我們?cè)贐lock中使用了self,那么這個(gè)Block就會(huì)把當(dāng)前實(shí)例捕獲,并持有,而當(dāng)前實(shí)例又擁有該Block,這就導(dǎo)致了循環(huán)引用的狀況.

如下修改:

 __weak ViewController * weakSelf=self;
    
    self.ablock=^(NSInteger count,NSString *str ){
        
        weakSelf.view.backgroundColor=[UIColor greenColor];
        
        return YES;
    };

我們?cè)谠揃lock中使用了self的若引用,從而打破了這個(gè)引用環(huán),解決了循環(huán)引用的問(wèn)題.

特別需要注意的一種情況是對(duì)在Block中使用成員變量,引起的循環(huán)引用問(wèn)題容易讓開(kāi)發(fā)者們所忽視,因?yàn)?在Block中雖然并沒(méi)有使用self,而是使用的成員變量,但是想要捕獲成員變量又一定要捕獲self,這依然會(huì)導(dǎo)致循環(huán)引用.例如:

//定義了一個(gè)成員變量
{
    
    NSString *_chengyuanbianliang;
}
//在Block中使用成員變量,導(dǎo)致循環(huán)引用
self.ablock=^(NSInteger count,NSString *str ){
        
        NSLog(@"%@",_chengyuanbianliang);
        
        return YES;
    };

我們可以如下解決這個(gè)問(wèn)題:


    __weak ViewController * weakSelf=self;
    
    self.ablock=^(NSInteger count,NSString *str ){
      __strong  ViewController *strongSelf=weakSelf;
        
        NSLog(@"%@",strongSelf->_chengyuanbianliang );
        
        return YES;
    };

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末谎僻,一起剝皮案震驚了整個(gè)濱河市娄柳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌艘绍,老刑警劉巖赤拒,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異鞍盗,居然都是意外死亡需了,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)般甲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)肋乍,“玉大人,你說(shuō)我怎么就攤上這事敷存∧乖欤” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵锚烦,是天一觀的道長(zhǎng)觅闽。 經(jīng)常有香客問(wèn)我,道長(zhǎng)涮俄,這世上最難降的妖魔是什么蛉拙? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮彻亲,結(jié)果婚禮上孕锄,老公的妹妹穿的比我還像新娘。我一直安慰自己苞尝,他們只是感情好畸肆,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著宙址,像睡著了一般轴脐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上抡砂,一...
    開(kāi)封第一講書(shū)人閱讀 49,816評(píng)論 1 290
  • 那天大咱,我揣著相機(jī)與錄音,去河邊找鬼注益。 笑死徽级,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的聊浅。 我是一名探鬼主播餐抢,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼现使,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了旷痕?” 一聲冷哼從身側(cè)響起碳锈,我...
    開(kāi)封第一講書(shū)人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎欺抗,沒(méi)想到半個(gè)月后售碳,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绞呈,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年贸人,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片佃声。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡艺智,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出圾亏,到底是詐尸還是另有隱情十拣,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布志鹃,位于F島的核電站夭问,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏曹铃。R本人自食惡果不足惜缰趋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望陕见。 院中可真熱鬧秘血,春花似錦、人聲如沸淳玩。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蜕着。三九已至,卻和暖如春红柱,著一層夾襖步出監(jiān)牢的瞬間承匣,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工锤悄, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留韧骗,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓零聚,卻偏偏與公主長(zhǎng)得像袍暴,于是被迫代替她去往敵國(guó)和親些侍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

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