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á)式:
省略返回值類型時(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á)式:
如果不使用參數(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)