1.什么是Block
帶有局部變量的匿名函數(shù)痴昧,與C語言中的函數(shù)指針類似,可當(dāng)做參數(shù)進(jìn)行傳值冠王,且可以沒有名字赶撰。
格式如下:
- block的代碼是內(nèi)聯(lián)的,效率高于函數(shù)調(diào)用柱彻;
- block對(duì)于外部變量默認(rèn)是只讀屬性豪娜;
- block被Objective-C看成是對(duì)象處理。
2.Block的使用
1.無參數(shù)無返回值
//1哟楷,無參數(shù)瘤载,無返回值,聲明和定義
void(^MyBlockOne)(void) = ^(void){
NSLog(@"無參數(shù)卖擅,無返回值");
};
MyBlockOne();//block的調(diào)用
2.有參數(shù)無返回值
//2鸣奔,有參數(shù),無返回值惩阶,聲明和定義
void(^MyblockTwo)(int a) = ^(int a){
NSLog(@"@ = %d我就是block挎狸,有參數(shù),無返回值",a);
};
MyblockTwo(100);
3.有參數(shù)有返回值
//3断楷,有參數(shù)锨匆,有返回值
int(^MyBlockThree)(int,int) = ^(inta,intb){
NSLog(@"%d我就是block,有參數(shù)冬筒,有返回值",a + b);returna + b;
};
MyBlockThree(12,56);
4.無參數(shù)有返回值(很少用到)
//4恐锣,無參數(shù),有返回值
int(^MyblockFour)(void) = ^{NSLog(@"無參數(shù)舞痰,有返回值");
return45;
};
MyblockFour();
以上情況都是局部變量的block 參考鏈接
5.為避免使用同類型block時(shí)都要編輯大量代碼土榴,可以使用typedef 定義
typedef int (^MyBlock)(int , int);
這時(shí),MyBlock
就成為了一種Block類型
在定義類的屬性時(shí)可以這樣:
@property (nonatomic,copy) MyBlock myBlockOne;
使用時(shí):
self.myBlockOne = ^int (int ,int){
//TODO
}
現(xiàn)在假設(shè)一個(gè)使用場(chǎng)景响牛,B頁(yè)面要反向傳遞一個(gè)值到A頁(yè)面鞭衩。在B.h類中定義了一個(gè)屬性的@property (nonatomic,copy) void (^myBlock)(NSString * data);
学搜,在B.m中調(diào)用的時(shí)候一般要進(jìn)行一個(gè)判斷(學(xué)習(xí)的時(shí)候大家應(yīng)該都會(huì),啰嗦下~)
if (self. myBlock) {
self. myBlock(//需要傳遞的對(duì)象值);
}
在A.m中接受傳遞值的時(shí)候:
B.myBlock = ^(NSString * data){
//TODO
//data 就是B頁(yè)面?zhèn)鬟f到A頁(yè)面的數(shù)據(jù)
}
//B 為B頁(yè)面實(shí)例化的對(duì)象
6.截獲自動(dòng)變量(自動(dòng)變量=局部變量)
看下面代碼:
int main()
{
int dmy = 256;
int val = 10;
const char *fmt = "val = %d\n";
void (^blk)(void) = ^{printf(fmt, val);};
val = 2;
fmt = "These values were changed. val = %d\n";
blk();
return 0;
}
執(zhí)行結(jié)果:val = 10
解釋:在該源代碼中论衍,Block語法的表達(dá)式使用的是它之前聲明的自動(dòng)變量fmt 和val.Block語法中瑞佩,Block表達(dá)式截獲的自動(dòng)變量,已保存該自動(dòng)變量瞬間的值坯台。因?yàn)锽lock表達(dá)式保存了自動(dòng)變量的值炬丸,所以在執(zhí)行Block語法之后,即使概念Block中的自動(dòng)變量的值也不會(huì)影響B(tài)lock執(zhí)行時(shí)自動(dòng)變量的值蜒蕾。這就是所謂的截獲
7.用_ _block修飾符修飾稠炬,改變局部變量的值
int val = 0;
void (^block)(void) = ^{
val = 1;
}
block();
NSLog(@"val = %d",val);
它會(huì)直接報(bào)錯(cuò) error: variable is not assignable (missing __block type specifier)
這時(shí)候就是在告訴您要想改變Block中局部變量,可使用__block 修飾符修飾變量咪啡。
__block int val = 0;
void (^block)(void) = ^{
val = 1;
}
block();
NSLog(@"val = %d",val); //val的值為1
還有一種純屬使用的情況首启,不改變值,如:
id array = [[NSMutableArray alloc] init];
void (^blk)(void) = ^{
id obj = [[NSObject alloc] init];
[array addObject:obj];
};
沒有向array賦值撤摸,僅僅是使用毅桃,是截獲到了NSMutableArray類對(duì)象的結(jié)構(gòu)體指針,不會(huì)報(bào)錯(cuò)准夷;當(dāng)向它賦值時(shí)就會(huì)編譯報(bào)錯(cuò)钥飞。
8. 使用__weak關(guān)鍵字,避免循環(huán)使用造成內(nèi)存泄漏
假設(shè)使用場(chǎng)景:在二級(jí)頁(yè)面B中定義了Block屬性@property (nonatomic,copy) void (^stateBlock)(NSString * data);
在B.m中調(diào)用了block衫嵌,self.stateBlock(//需要傳遞的值);
在A.m中
- (void)buttonAction {
B *myVC = [[B alloc] init];
[self presentViewController:myVC animated:YES completion:^{
}];
__weak typeof(self) weakSelf = self;//防止循環(huán)引用
//用屬性定義的注意:這里屬性是不會(huì)自動(dòng)補(bǔ)全的读宙,方法就會(huì)自動(dòng)補(bǔ)全
[myVC.stateBlock = ^(NSString * data){
weakSelf.labelA.text = data;
}];
}
9. Block的回調(diào)
開發(fā)者在block沒發(fā)布前,實(shí)現(xiàn)回調(diào)基本都是通過代理的方式進(jìn)行的楔绞。比如負(fù)責(zé)網(wǎng)絡(luò)請(qǐng)求的原生類NSURLConnection類结闸,通過多個(gè)協(xié)議方法實(shí)現(xiàn)請(qǐng)求中的事件處理。而在最新的環(huán)境下酒朵,使用的NSURLSession已經(jīng)采用block的方式處理任務(wù)請(qǐng)求了膀估。各種第三方網(wǎng)絡(luò)請(qǐng)求框架也都在使用block進(jìn)行回調(diào)處理。這種轉(zhuǎn)變很大一部分原因在于block使用簡(jiǎn)單耻讽,邏輯清晰刻蚯,靈活等原因秉撇。接下來我會(huì)完成一次網(wǎng)絡(luò)請(qǐng)求亩歹,然后通過block進(jìn)行回調(diào)處理象迎。這些回調(diào)包括請(qǐng)求完成
按照returnValue(^blockName)(parameters)的方式進(jìn)行block的聲明未免麻煩了些,我們可以通過關(guān)鍵字typedef來為block起類型名稱慰枕,然后直接通過類型名進(jìn)行block的創(chuàng)建:
//DownloadManager.h
#import <Foundation/Foundation.h>
@interface DownloadManager : NSObject <NSURLSessionDownloadDelegate>
// block 重命名
typedef void (^DownloadHandler)(NSData * receiveData, NSError * error);
- (void)downloadWithURL:(NSString *)URL parameters:(NSDictionary *)parameters handler:(DownloadHandler)handler ;
@end
//DownloadManager.m
#import "DownloadManager.h"
@implementation DownloadManager
- (void)downloadWithURL:(NSString *)URL parameters:(NSDictionary *)parameters handler:(DownloadHandler)handler
{
NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:URL]];
NSURLSession * session = [NSURLSession sharedSession];
//執(zhí)行請(qǐng)求任務(wù)
NSURLSessionDataTask * task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (handler) {
dispatch_async(dispatch_get_main_queue(), ^{
handler(data,error);
});
}
}];
[task resume];
}
上面通過封裝NSURLSession的請(qǐng)求具则,傳入一個(gè)處理請(qǐng)求結(jié)果的block對(duì)象,就會(huì)自動(dòng)將請(qǐng)求任務(wù)放到工作線程中執(zhí)行實(shí)現(xiàn)具帮,我們?cè)诰W(wǎng)絡(luò)請(qǐng)求邏輯的代碼中調(diào)用如下:
- (IBAction)buttonClicked:(id)sender {
#define SOGOUURL @"http://dlsw.baidu.com/sw-search-sp/soft/9d/25765/sogou_mac_32c_V3.2.0.1437101586.dmg"
//下載類
DownloadManager * downloadManager = [[DownloadManager alloc] init];
[downloadManager downloadWithURL: SOGOUURL parameters:nil handler:^(NSData *receiveData, NSError *error) {
if (error) {
NSLog(@"下載失敳├摺:%@",error);
}else {
NSLog(@"下載成功低斋,%@",receiveData);
}
}];
}