這篇文章是為了講解ReactiveCocoa做的一個(gè)鋪墊,由于ReactiveCocoa中大量使用Block,所以這里將對(duì)Block進(jìn)行一個(gè)鞏固,當(dāng)然,Block用的很熟悉的朋友,大可不必看這篇文章。
在iOS4.0之后,Block面世,它是一種特殊的數(shù)據(jù)類型。它的本身是一段代碼,特殊在于,它將這段代碼當(dāng)做了變量,然后通過(guò)類似block()這種方式回調(diào)。
寫一個(gè)Block總共分幾步?
答:三步!
-
聲明:
NSString *(^blockName)(NSString *str1, NSString *str2);
從前到后依次為返回值類型、block名字、參數(shù)1、參數(shù)2; - 定義:
(NSString *)^(NSString *str1, NSString *str2) {
return [NSString stringWithFormat:@"%@+%@",str1, str2];
};
注意:Block是一個(gè)語(yǔ)法塊,后面我們要帶“;”的!
-
調(diào)用:
blockName(@"str1",@"str2");
根據(jù)傳入?yún)?shù)和返回值類型,我們可以認(rèn)為Block有四種類型:
- 有參有返回值
//聲明&定義
NSString *(^block5)(NSString *) = ^(NSString *str){
return [NSString stringWithFormat:@"%@+參數(shù)2", str];
};
//調(diào)用
NSLog(@"%@", block5(@"參數(shù)1"));
- 有參無(wú)返回值
//聲明&定義
void (^block2)(NSString *) = ^(NSString *str){
NSLog(@"%@", str);
};
//調(diào)用
block2(@"參數(shù)");
- 無(wú)參有返回值
//聲明&定義
NSString *(^block4)(void) = ^(void){
return @"返回值";
};
//調(diào)用
NSLog(@"%@", block4());
- 無(wú)參無(wú)返回值
//聲明&定義
void (^block1)(void) = ^(void){
NSLog(@"無(wú)參數(shù) 無(wú)返回值!");
};
//調(diào)用
block1();
Block的使用
在Block誕生之前,開(kāi)發(fā)者們用的都是delegate來(lái)完成回調(diào)。在Block面世之后,絕大部分的回調(diào)處理被改為了Block。之所以做出這一改變,跟Block的使用簡(jiǎn)單,靈活等原因是分不開(kāi)的!
這里我以再次封裝AFNetworking為例,來(lái)對(duì)Block回調(diào)進(jìn)行講解。
先分析一下網(wǎng)絡(luò)請(qǐng)求的幾個(gè)必要要素:
- 域名
- 請(qǐng)求參數(shù)
- 網(wǎng)絡(luò)請(qǐng)求成功回調(diào)
- 網(wǎng)絡(luò)請(qǐng)求失敗回調(diào)
- 網(wǎng)絡(luò)異?;卣{(diào)
這里面前兩者沒(méi)什么可分析的,主要說(shuō)一下后三個(gè)Block的機(jī)制。
三者我們著重來(lái)講解一個(gè)(網(wǎng)絡(luò)請(qǐng)求成功回調(diào)),因?yàn)槿咧皇菂?shù)和返回的數(shù)據(jù)類型不一樣,大同小異,只要理解了,沒(méi)必要多做解釋。
- 通過(guò)typedef來(lái)給block起一個(gè)類型名稱
typedef void (^ReturnValueBlock) (id returnValue);//成功回調(diào)
typedef void (^ErrorCodeBlock) (id errorCode);//失敗回調(diào)
typedef void (^FailureBlock)();//錯(cuò)誤回調(diào)
當(dāng)我們要回調(diào)的時(shí)候,我們可以根據(jù)我們的參數(shù)類型、個(gè)數(shù),和返回值類型來(lái)選擇我們用哪種Block。
在網(wǎng)絡(luò)請(qǐng)求的類中聲明這個(gè)方法:
#pragma --mark POST請(qǐng)求方式
-(void) NetRequestPOSTWithRequestURL: (NSString *) requestURLString
WithParameter: (NSDictionary *) parameter
WithReturnValeuBlock: (ReturnValueBlock) block
WithErrorCodeBlock: (ErrorCodeBlock) errorBlock
WithFailureBlock: (FailureBlock) failureBlock;
方法中,我們傳入的參數(shù)有三個(gè)Block,他們?nèi)齻€(gè)分別是用之前用typedef聲明的,分別處理網(wǎng)絡(luò)請(qǐng)求的時(shí)候不同的處理。接下來(lái)看看它是怎么實(shí)現(xiàn)的
#pragma --mark POST請(qǐng)求方式
-(void) NetRequestPOSTWithRequestURL: (NSString *) requestURLString
WithParameter: (NSDictionary *) parameter
WithReturnValeuBlock: (ReturnValueBlock) block
WithErrorCodeBlock: (ErrorCodeBlock) errorBlock
WithFailureBlock: (FailureBlock) failureBlock
{
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager POST:[NSString stringWithFormat:@"%@%@", BaseUrl, requestURLString] parameters:parameter progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"requestUrl: %@\nparameter: %@", requestURLString, parameter);
if ([[responseObject objectForKey:@"status"] integerValue]== 200) {
block(responseObject);
} else if ([[responseObject objectForKey:@"status"] integerValue]== 400) {
errorBlock(responseObject);
}
} failureObjc:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error, id responseObject) {
NSLog(@"%@", responseObject);
failureBlock();
}];
}
主要看Success里面,這里面回調(diào)了兩個(gè)block:”block“、”errorBlock“。不知道大家能不能看這兩個(gè)block是在什么時(shí)候回調(diào)的。感覺(jué)應(yīng)該可以看得懂,根據(jù)網(wǎng)絡(luò)請(qǐng)求返回的狀態(tài),”200“表示網(wǎng)絡(luò)請(qǐng)求成功,”400“表示網(wǎng)絡(luò)請(qǐng)求失敗,各家公司的接口應(yīng)該都差不多吧。接下來(lái)就是使用了
由于這段代碼是從公司工程中拿出來(lái)的,所以接口和數(shù)據(jù)的處理都被我刪掉重新簡(jiǎn)化的寫了下
-(void)getTeacherCommentLis{
NSDictionary *dic = @{@"access_token":[USER_DEFAULT objectForKey:@"access_token"],
@"pageNo":[NSNumber numberWithInteger:1]};
[[ZTAPIClient alloc] NetRequestPOSTWithRequestURL:@"getTeacherCommentLis" WithParameter:dic WithReturnValeuBlock:^(id returnValue) {
網(wǎng)絡(luò)請(qǐng)求成功,得到返回結(jié)果;
可以刷新TableView,存儲(chǔ),更新等一系列操作
} WithErrorCodeBlock:^(id errorCode) {
網(wǎng)絡(luò)請(qǐng)求失敗,往往是填寫的參數(shù)有問(wèn)題
} WithFailureBlock:^{
}];
}
現(xiàn)在來(lái)想一想,回顧一下,以上的操作我們都是在做什么。我們是在哪里聲明的block、又是在哪里定義的、最后又是在哪里調(diào)用的?
- 在網(wǎng)絡(luò)請(qǐng)求類的聲明文件中,我們用typedef聲明了block;
- 在網(wǎng)絡(luò)請(qǐng)求的方法中,我們把聲明的block當(dāng)做了參數(shù),傳入到了方法中
- 在調(diào)用方法的時(shí)候,我們定義了這個(gè)block需要做的操作
- 在網(wǎng)絡(luò)請(qǐng)求類實(shí)現(xiàn)方法中,我們調(diào)用了block
Block相關(guān)的幾個(gè)關(guān)鍵字
copy
block在使用的過(guò)程中跟對(duì)象一樣會(huì)引起引用計(jì)數(shù)變化。同樣聲明block時(shí),它的內(nèi)存是分配在棧上,隨時(shí)可能被回收,所以,我們需要將他拷貝到堆上。通過(guò)copy可以把block拷貝到堆上,來(lái)保證block不會(huì)被隨時(shí)回收。
@property (nonatomic, copy)void(^block)(NSString *);
__block
在一個(gè)block里面,我們對(duì)block之外的變量是只讀操作,也就是我們只能讀取它的值,不可以更改。像這樣,Xcode會(huì)給你報(bào)一個(gè)這樣的錯(cuò)誤。

說(shuō)到__block, 它是用于,我們想在block內(nèi)來(lái)改變某個(gè)外部的變量的時(shí)候,我們需要在聲明這個(gè)變量的時(shí)候用“__block”來(lái)修飾。

__weak
有時(shí)在使用block 的時(shí)候,由于self 是被強(qiáng)引用的,在 ARC 下,當(dāng)編譯器自動(dòng)將代碼中的block從??截惖蕉褧r(shí),block 會(huì)強(qiáng)引用和持有self,而 self 也強(qiáng)引用和持有了 block,這就造成了循環(huán)引用,導(dǎo)致兩者都不能釋放,內(nèi)存泄露。__weak解決循環(huán)引用的理念就是,將其中一者弱化,編程弱引用,也就是引用計(jì)數(shù)不會(huì)增加。 __weak修飾符修飾變量 self,讓 block 不強(qiáng)引用 self,從而破除循環(huán)。