iOS中Blocks的使用方法

圖片來自:[秋水飲馬](http://www.lxweimin.com/u/a7f876850fa6)
Blocks是“帶有自動(dòng)變量(即局部變量)的匿名函數(shù)”。實(shí)質(zhì)是Objective-C對閉包的對象實(shí)現(xiàn),簡單說來,Block就是對象。

NO.1 Blocks的語法:

  • Blocks的語法

1. Block類型變量(block的聲明)

在Block語法下,可將Block語法賦值給聲明為Block類型的變量中(即源代碼中一旦使用Block語法就相當(dāng)于生成了可賦值給Block類型變量的“值”)。
??:

int (^blk) (int);
“Block”即指源代碼中的Block語法,也指由Block語法所生成的值。
  • 使用Block語法將Block賦值為Block類型變量。

??:

int (^blk) (int) = ^ int (int count){
        return count +1;
    };
  • 由Block類型變量向Block類型變量賦值。

??:

int (^blk1) (int) = blk;
int (^blk2) (int);
 blk2 = blk1;
  • Block類型變量作為函數(shù)參數(shù)傳遞。

??:

-(void)blockFunc:(int (^)(int))blk;
  • Block類型變量作為函數(shù)返回值返回。

??:

-(int)blockFunc1{
    int (^blk) (int)  = ^(int count){
        return count +1;
    };
    return blk(1);
}
//等同于:
-(int)blockFunc1{
    return ^(int count){
        return count +1;
    }(1);
}
  • Block類型變量作為函數(shù)參數(shù)和返回值時(shí),可以通過typedef為Block類型提供別名,從而起到簡化塊類型變量名的作用。

??:

typedef int (^BLK) (int);//
-(void)blockFunc:(BLK)blk;//作為函數(shù)參數(shù)
-(int)blockFunc1{
    BLK blk = ^(int count){
        return count +1;
    };
    return blk(1);
}//作為返回值

2.Block常量表達(dá)式(block的實(shí)現(xiàn)):

  • 圖片來自:《Objective-C高級編程:iOS與OS X多線程和內(nèi)存管理》

??:

^ int (int count){
        return count +1;
    };
  • 省略返回值類型的Block常量表達(dá)式:

圖片來自:《Objective-C高級編程:iOS與OS X多線程和內(nèi)存管理》

省略返回值類型時(shí),如果表達(dá)式有return語句就返回該返回值的類型。如果表達(dá)式?jīng)]有return語句就返回void類型。如果表達(dá)式有多個(gè)return語句,所有的return語句返回值的類型必須相同。
??:

^ int (int count){
        return count +1;
    };

省略int類型返回值:

^ (int count){
        return count +1;
    };

??:

^ void (int count){
        count +1;
        NSLog(@"%@",count+1);
    };

省略void類型:

^ (int count){
        count +1;
        NSLog(@"%@",count+1);
    };
  • 省略返回值類型和參數(shù)列表的Block常量表達(dá)式:
圖片來自:《Objective-C高級編程:iOS與OS X多線程和內(nèi)存管理》

如果不使用參數(shù),參數(shù)列表也可省略。
??:

^ void (void){
        NSLog(@"Hello world");
    };

省略void類型:

^ {
        NSLog(@"Hello world");
    };

NO.2 Blocks簡單的用法(有無參數(shù)及返回值)

  • 無參有返回的block
int(^Block1)()=^(){
     return 1;
};
Block1();
  • 無參無返回的block
void(^Block2)()=^(){
     NSLog(@"Block2");
};
Block2();
  • 有參有返回的block
int (^Block3)(int)=^(int num){
    return num;
};
Block3(3);
  • 有參無返回的block
void (^Block4)(int)=^(int num){
    NSLog(@"%d",num);
};
Block4(4);

NO.3 block的主要用法(主要用于對象之間的通信)

block的內(nèi)存不像OC中的類對象一樣在堆區(qū),而是在棧區(qū)。如果我們使用block作為一個(gè)對象的屬性,我們會(huì)使用關(guān)鍵字copy修飾,系統(tǒng)會(huì)把該 block的實(shí)現(xiàn)拷貝一份到堆區(qū),這樣我們對應(yīng)的屬性,就擁有的該block的所有權(quán)。

棧上的Block會(huì)復(fù)制到堆時(shí),Block上的 __block變量跟著復(fù)制到堆上。只要 __block變量在堆上繼續(xù)存在,那么對象就會(huì)繼續(xù)處于被持有的狀態(tài)。

1. 兩個(gè)Controller之間(A跳轉(zhuǎn)到B,B回調(diào)給A)

在B的.h中聲明block:

#import <UIKit/UIKit.h>
typedef void (^SelectedProvinceBlock)(WLZProvinceModel *province);//聲明block
@interface BViewController : UIViewController
@property (nonatomic,copy) SelectedProvinceBlock selectedProvince;//將block作為BViewController的一個(gè)屬性,使用copy將block復(fù)制到堆上
@end

在B的.m中回調(diào)block:

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (self.dataSourceArray.count != 0) {
        ProvinceModel *model = self.dataSourceArray[indexPath.section][indexPath.row];
        if (self.selectedProvince) {
            self.selectedProvince(model);
        }//回調(diào)block
        [self.navigationController popViewControllerAnimated:YES];
    }
}

在A中實(shí)現(xiàn)回調(diào)的block:

#pragma mark --- 獲取省份
- (void)addressButtonClick:(UIButton *)gesture
{
    
    WLZProvinceViewController *provinceVC = [[WLZProvinceViewController alloc] init];
    WS(weakSelf);
    //獲取省份
    [provinceVC setSelectedProvince:^(ProvinceModel *province) {

        //do something.
    }];
    [self.navigationController pushViewController:provinceVC animated:YES];
}

2. UIView與Controller之間(UView的點(diǎn)擊事件回調(diào)給Controller)

在UIVIew的.h中聲明block

#import <UIKit/UIKit.h>
typedef void (^RouteReloadButtonClick)();//聲明block
@interface TravelDetailHeaderView : UIView
@property (nonatomic,copy) RouteReloadButtonClick routeReloadButtonClick;//將block作為TravelDetailHeaderView的一個(gè)屬性,使用copy將block復(fù)制到堆上
@end

在UIVIew的.m中回調(diào)block

-(void)routeReloadClick:(UIButton *)sender
{
    if (self.routeReloadButtonClick) {
        self.routeReloadButtonClick();//block回調(diào)
    }
}

在Controller中實(shí)現(xiàn)回調(diào)的block:

    TravelDetailHeaderView *travelView = [[TravelDetailHeaderView alloc]initWithFrame:CGRectMake(0, 0, WIDTH, 83+WIDTH/5+60+30+25*self.AllStartCityModelArray.count)];
    travelView.routeReloadButtonClick = ^(){
        [weakSelf reloadRoutePlanning];//實(shí)現(xiàn)block
    };

3.Model與Controller或UIView之間(主要用來請求數(shù)據(jù)之后將數(shù)據(jù)回調(diào)給Controller或UIView展示)

在Model類的.h中聲明block

#import <Foundation/Foundation.h>

typedef void (^SucessBlock) (NSMutableArray *dataArray);//block聲明
typedef void (^FailureBlock) (NSError *error);//block聲明
@interface HotRouteAndClassicsModel : NSObject
//請求
@property (nonatomic,copy)SucessBlock sucessBlock;//將block作為HotRouteAndClassicsModel的一個(gè)屬性,使用copy將block復(fù)制到堆上
@property (nonatomic, copy)FailureBlock failureBlock;//將block作為HotRouteAndClassicsModel的一個(gè)屬性,使用copy將block復(fù)制到堆上

+(void)dealForHotRouteWithSucess:(SucessBlock)sucessBlock FailureBlock:(FailureBlock)failureBlock;//將block作為參數(shù)傳遞

@end

在Model類的.m中回調(diào)block

+(void)dealForHotRouteWithSucess:(SucessBlock)sucessBlock FailureBlock:(FailureBlock)failureBlock
{
    NSString *URLString =@"";//url接口
    NSDictionary *dic = [[NSDictionary alloc]initWithObjectsAndKeys: nil];//接口參數(shù)
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    manager.requestSerializer.timeoutInterval = 10;
    manager.requestSerializer = [AFHTTPRequestSerializer serializer];
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/plain", nil];

    [manager GET:URLString parameters:dic progress:^(NSProgress * _Nonnull downloadProgress) {
        
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSString *str = [[NSString alloc]initWithData:responseObject encoding:NSUTF8StringEncoding];
        if (str == nil || [str isEqualToString:@""]) {
            return ;
        }
        NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
        NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
        NSArray *modelArray = [travelArticleModel mj_objectArrayWithKeyValuesArray:[dic objectForKey:@""]];
        sucessBlock(modelArray);//block回調(diào)
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        failureBlock(error);//block回調(diào)
    }];
    
  
}

在Controller或UIView中實(shí)現(xiàn)回調(diào)的block

#pragma mark --- 數(shù)據(jù)處理
- (void)loadClassicRouteData
{
    [HotRouteAndClassicsModel dealForHotRouteWithSucess:^(NSMutableDictionary *dataDictionary) {
      //do something;
      //block 的實(shí)現(xiàn)
    } FailureBlock:^(NSError *error) {
        SLog(@"%@",error);
      //do something;
      //block 的實(shí)現(xiàn)
    }];
}
block要用copy修飾,還是用strong ?

block本身是像對象一樣可以retain,和release。但是,block在創(chuàng)建的時(shí)候,它的內(nèi)存是分配在棧(stack)上,而不是在堆(heap)上。他本身的作于域是屬于創(chuàng)建時(shí)候的作用域,一旦在創(chuàng)建時(shí)候的作用域外面調(diào)用block將導(dǎo)致程序崩潰。
使用retain也可以,但是block的retain行為默認(rèn)是用copy的行為實(shí)現(xiàn)的,

因?yàn)閎lock變量默認(rèn)是聲明為棧變量的,為了能夠在block的聲明域外使用,所以要把block拷貝(copy)到堆,所以說為了block屬性聲明和實(shí)際的操作一致,最好聲明為copy。

總結(jié): MRC 下用 copy,ARC 下都可以。

block如何避免循環(huán)引用

在Blocks中,Block常量表達(dá)式會(huì)截獲所使用的自動(dòng)變量的值(即保存該自動(dòng)變量的瞬間值)。

- (void)viewDidLoad {
    [super viewDidLoad];
    
    int a = 10;//a對于_block來說就是一個(gè)外部變量
    
    _block = ^{
        NSLog(@"a = %d",a);//但是,此時(shí)是可以使用a的。
    };
    
}```
附有 \_\_block說明符的自動(dòng)變量可在Block中賦值。

__block int a = 10;//用__block修飾之后,系統(tǒng)會(huì)傳遞a的地址(&a)

_block = ^{
    
    a += 20;
    NSLog(@"a = %d",a);//有地址,當(dāng)然就可以修改a的值了。此時(shí)a的值是30
};
若由于Block引發(fā)了循環(huán)引用時(shí),在ARC有效時(shí),使用\_\_weak修飾符來避免Block中的循環(huán)引用。ARC無效時(shí),\_\_block說明符被用來避免Block中的循環(huán)引用。

  • (void)viewDidLoad {
    [super viewDidLoad];

    UIView *view = [[UIView alloc] init];

    __weak typeof(view)_view = view;//_view和view指向同一塊內(nèi)存,而_view是弱引用,view的retainCount還是1.

    _block = ^{
    //view.frame = CGRectMake(0, 0, 100, 100);//在block內(nèi)部使用view對象,系統(tǒng)會(huì)對view強(qiáng)引用,此時(shí)會(huì)造成內(nèi)存泄漏。
    _view.frame = CGRectMake(0, 0, 100, 100);
    };

}

> ###\_\_block與\_\_weak
> - \_\_block修飾對象和基本數(shù)據(jù)類型, 而\_\_weak只能修飾對象. 
> - \_\_block不管是ARC還是MRC模式下都可以使用,\_\_weak只能在ARC模式下使用
> - \_\_block對象可在block中修改(重新賦值), 而\_\_weak不行. 
> - 通常在Block中使用\_\_weak來修飾self從而防止循環(huán)引用

//防止循環(huán)引用

define WS(weakSelf) __weak __typeof(&*self)weakSelf = self;






#參考文章
 - [**《Objective-C高級編程》Blocks**](http://www.lxweimin.com/p/2cb10b5c2838)

- [** iOS開發(fā)之手把手教你使用Block **](http://www.lxweimin.com/p/d74ee9dd219c)

- [**iOS開發(fā)-由淺至深學(xué)習(xí)block**](http://www.cocoachina.com/ios/20160414/15921.html)

- [**[iOS開發(fā)之---block的使用(詳解)] **](http://www.cnblogs.com/libaoshen/p/5861466.html)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,667評論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,635評論 2 380

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