一、基本概念:
Block是一種C語言的數(shù)據(jù)類型,指向結(jié)構(gòu)體的指針,平常我們將Block當(dāng)作一個代碼段使用,相當(dāng)于函數(shù),Block相比于函數(shù)的優(yōu)點是 Block可以當(dāng)做一個參數(shù)傳遞而函數(shù)/方法不能.。我們可以用Block寫一些函數(shù)語句,可以使用多線程。Block也可以作為一個回調(diào),不僅包含回調(diào)期間的代碼,也包含了執(zhí)行期間需要的數(shù)據(jù)。類似于"閉包",閉包的定義是可以從函數(shù)外部訪問函數(shù)內(nèi)部的變量。
二、基本用法:
1)無參無返回值(最簡單形式)
定義格式:void (^block名)() = ^{代碼塊;};
使用格式:block名();
2)無參有返回值(以返回值為int為例)
定義格式:int (^block名)() = ^{代碼塊;};
使用格式:int result = block名();
3)有參無返回值(以參數(shù)為int為例)
定義格式:void (^block名)(int) = ^(int a){代碼塊;};
使用格式:? block名(6);
4)有參有返回值(以參數(shù)、返回值為int為例;可以有多個參數(shù))
定義格式:int (^block名)(int) = ^(int a){代碼塊;};
使用格式:int result = block名(6);
5)利用typedef定義block類型(和指向函數(shù)的指針很像)
定義格式:typedef 返回值類型(^新別名)(參數(shù)類型列表);
例:typedef int (^myBlock)(int,int);
三、Block訪問外部變量
1)在block內(nèi)部可以訪問block外部的變量,但是,這是一個新的內(nèi)存空間變量;
block內(nèi)部也可以定義和block外部同名的變量,此時這個局部變量會屏蔽外部變量的作用域。
2)如果想要在block內(nèi)部修改外部的局部變量,需要給該變量加上__block關(guān)鍵字;原因是加__block,block會引用常量變量的地址,則可以任意修改該變量指向的內(nèi)容。如果不加__block直接在block內(nèi)部修改變量,會編譯報錯,block內(nèi)部變量是只讀的。
四、Block使用技巧和注意事項
1)block的快速提示,輸入inlineBlock 會有代碼提示;
2)block變量用作方法的參數(shù)時,最好把參數(shù)類型列表部分加上具體的參數(shù)名
3)利用block遍歷取值
例: [dict enumerateKeysAndObjectsUsingBlock:^(id? _Nonnull key, id? _Nonnull obj, BOOL * _Nonnull stop) {
}];
5)? block修飾:block的發(fā)展也經(jīng)過了MRC和ARC兩個階段;
MRC情況下:用copy修飾后,如果要在block內(nèi)部使用對象,則需要進(jìn)行__block typeof(Target) blockTarget = Target處理,在block里面用blockTarget 操作。
ARC情況下:如果用copy修飾,該block就會存儲在堆空間以避免在使用時發(fā)生釋放。但會對block的內(nèi)部對象進(jìn)行強(qiáng)引用,導(dǎo)致循環(huán)引用,內(nèi)存無法釋放。 所以block使用 self ,要使用 self 弱引用寫法. 即__weak typeof(self) weakSelf = self;如果用weak修飾block,該block就會存放在??臻g,雖不會出現(xiàn)循環(huán)引用,但容易造成需要使用對象時,對象已經(jīng)不存在的問題,原因是存儲在棧空間的各種數(shù)據(jù)是系統(tǒng)自動處理的,并不需要程序猿手動處理,所以我們也不知道對象什么時候會被釋放。
6)循環(huán)引用
分析循環(huán)(VC代表viewController控制器)
已知 : op --> self(VC) 強(qiáng)引用
如果 self 強(qiáng)引用的對象,對 op 也有強(qiáng)用.那么就會有循環(huán)引用
self(VC) --> queue/OPCache --> op --> self(VC)
所以block使用 self ,要使用 self 弱引用寫法. 即__weak typeof(self) weakSelf = self;
GCD中的block(任務(wù))出現(xiàn)self,會造成循環(huán)引用嗎?? ----- GCD中的block可以直接使用 self
block循環(huán)引用的條件: block------->強(qiáng)應(yīng)用(self)? self ------->強(qiáng)引用(block屬性)
NSOperation 中的blcok中是否可以出現(xiàn) self ?
一般情況下 NSOperationQueue 需要作為一個屬性. 將操作添加到操作隊列中!
注意: 1.確實會出現(xiàn)循環(huán)引用!
但是: 操作一旦執(zhí)行完畢之后,就會被自動銷毀! 所以 NSOperation 中可以出現(xiàn) self.
7)block可以在兩個對象之間進(jìn)行傳值,類似于代理和通知;
以POST請求為例,假如我在A對象中封裝了POST請求的方法,該方法請求回來的數(shù)據(jù)想傳遞給B對象,就可以利用block實現(xiàn):
代碼示例:
"A對象中聲明方法
//MARK:- 一句話封裝POST請求
- (void)POSTRequestWithURLString:(NSString *)urlString andDict:(NSDictionary *)pramarts andSuccessBlock:(successBlock)successblock andFalseBlock:(falseBlock)falseblock ;
"實現(xiàn)方法
- (void)POSTRequestWithURLString:(NSString *)urlString andDict:(NSDictionary *)pramarts andSuccessBlock:(successBlock)successblock andFalseBlock:(falseBlock)falseblock {
//MARK:- 創(chuàng)建請求
NSURL *url = [NSURL URLWithString:urlString];
#warning - POST請求要創(chuàng)建可變請求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:15];
//MARK:- 設(shè)置請求體、請求方法等參數(shù)
request.HTTPMethod = @"POST";
NSMutableString *strM = [NSMutableString string];
[pramarts enumerateKeysAndObjectsUsingBlock:^(id? _Nonnull key, id? _Nonnull obj, BOOL * _Nonnull stop) {
NSString *pramKey = key;
NSString *pramValue = obj;
[strM appendFormat:@"%@=%@&",pramKey,pramValue];
}];
NSString *pramart = [strM substringToIndex:strM.length-1];
request.HTTPBody = [pramart dataUsingEncoding:NSUTF8StringEncoding];
//MARK:- 發(fā)送請求
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (data&&!error) {
if (successblock!=nil) {//一定要先判斷block是否為空
successblock( obj,response);//回調(diào)
}
}else{
if (falseblock!=nil) {//同上
falseblock(error);
}
}
}]resume];
}
"B對象調(diào)用方法
[[YQPOSTRequest sharedPOSTRequest] POSTRequestWithURLString:urlStr andDict:pram andSuccessBlock:^(NSData *data, NSURLResponse *response) {
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
} andFalseBlock:^(NSError *error) {
NSLog(@"%@",error);
}];
五、解決OC中block循環(huán)引用的三種方式
1)解決循環(huán)引用的第一種方式
//iOS 5.0 引用來解決循環(huán)引用的方式? 和weak屬性關(guān)鍵字作用類似
//當(dāng)對象被系統(tǒng)回收時? 對象的地址 會自動指向 nil? 不會出現(xiàn)野指針訪問
__weak typeof(self) weakSelf = self;
2)解決循環(huán)引用的第二種方式
//? ? __weak typeof(self) weakSelf = self;
//會引起 EXC_BAD_ACCESS 錯誤 是MRC 時代最常見的錯誤? 野指針? --> 壞地址訪問
// 和 assgin屬性關(guān)鍵字的作用類似? 對象被系統(tǒng)回收時 對象的地址不會自動指向nil
// iOS4.0 和block 一起推出的 用來解決循環(huán)引用的
__unsafe_unretained typeof(self) weakSelf = self;
3) 解決循環(huán)引用的第三種方式
//weak-strong-dance wwdc 推出的解決方式? 在AFN中被大量的運(yùn)用到
__weak typeof(self) weakSelf = self;
[self.tools loadData:^(NSString *result) {
//閉包中對弱引用的weakSelf 在強(qiáng)引用一下
__strong typeof(weakSelf) strongSelf = weakSelf;
NSLog(@"%@ %@",result,strongSelf);
}];