作者:J_Knight_
鏈接:http://www.itdecent.cn/p/89dd444399ce
來源:簡書
一.概述
基于 AFNetworking 進(jìn)行再一次封裝,以及緩存處理。采用了離散式替代傳統(tǒng) AFNetworking 的集約式編程。通過給每一個請求創(chuàng)建一個對象的高度定制性,使 url 以及響應(yīng)方式等內(nèi)容不暴露出來,以增加了一些代碼工作量為代價,更好的做到業(yè)務(wù)層與網(wǎng)絡(luò)層的分離。
二.架構(gòu)

說明:
- YTKNetwork 框架將每一個請求實例化,YTKBaseRequest 是所有請求類的基類,YTKRequest 是它的子類。所以如果我們想要發(fā)送一個請求,則需要創(chuàng)建并實例化一個繼承于YTKRequest的自定義的請求類(CustomRequest)并發(fā)送請求。
- YTKNetworkAgent 是一個單例,負(fù)責(zé)管理所有的請求類(例如 CustomRequest )。當(dāng) CustomRequest 發(fā)送請求以后,會把自己放在 YTKNetworkAgent 持有的一個字典里,讓其管理自己。
- 我們說 YTKNetwork 封裝了 AFNetworking ,實際上是 YTKNetworkAgent 封裝了 AFNetworking ,由它負(fù)責(zé) AFNetworking 請求的發(fā)送和 AFNetworking 的回調(diào)處理。所以如果我們想更換一個第三方網(wǎng)絡(luò)請求庫,就可以在這里更換一下。而 YTKRequest 更多的是只是負(fù)責(zé)緩存的處理。
三.設(shè)計模式
采用命令模式(Command Pattern)設(shè)計模式。
命令模式的簡單介紹:
- Client:創(chuàng)建具體命令
- Invoker:命令調(diào)用者
- Receiver:命令接受者
- ConcreteCommand:具體命令
- Command:抽象命令
在 YTKNetwork 中的對應(yīng)應(yīng)用如下表:

命令模式的本質(zhì)是對命令的封裝,將發(fā)出命令的責(zé)任和執(zhí)行命令的責(zé)任分割開。
命令模式允許請求的一方和接收的一方獨立開來,使得請求的一方不必知道接收請求的一方的接口,更不必知道請求是怎么被接收,以及操作是否被執(zhí)行、何時被執(zhí)行,以及是怎么被執(zhí)行的。
四.YTKNetwork 各類介紹
| 類名 | 職責(zé) |
|---|---|
| YTKBaseRequest | 所有請求類的基類。持有 NSURLSessionTask 實例,responseData,responseObject,error 等重要數(shù)據(jù),提供一些需要子類實現(xiàn)的與網(wǎng)絡(luò)請求相關(guān)的方法,處理回調(diào)的代理和 block,命令 YTKNetworkAgent 發(fā)起網(wǎng)絡(luò)請求。 |
| YTKRequest | YTKBaseRequest 的子類。負(fù)責(zé)緩存的處理:請求前查詢緩存;請求后寫入緩存。 |
| YTKNetworkConfig | 被 YTKRequest 和 YTKNetworkAgent 訪問。負(fù)責(zé)所有請求的全局配置,例如 baseUrl 和 CDNUrl 等等。 |
| YTKNetworkPrivate | 提供 JSON 驗證,appVersion 等輔助性的方法;給 YTKBaseRequest 增加一些分類。 |
| YTKNetworkAgent | 真正發(fā)起請求的類。負(fù)責(zé)發(fā)起請求,結(jié)束請求,并持有一個字典來存儲正在執(zhí)行的請求。 |
| YTKBatchRequest | 負(fù)責(zé)管理多個 YTKBatchRequest 實例,持有一個數(shù)組來保存 YTKBatchRequest 。支持添加和刪除 YTKBatchRequest 實例。 |
| YTKChainRequest | 可以發(fā)起鏈?zhǔn)秸埱?,持有一個數(shù)組來保存所有的請求類。當(dāng)某個請求結(jié)束后才能發(fā)起下一個請求,如果其中有一個請求返回失敗,則認(rèn)定本請求鏈?zhǔn) ?/td> |
| YTKChainRequestAgent | 負(fù)責(zé)管理多個 YTKChainRequestAgent 實例,持有一個數(shù)組來保存 YTKChainRequest。支持添加和刪除 YTKChainRequest 實例。 |
五.基本使用
單個請求配置
官方的教程建議我們將請求的全局配置是在 AppDelegate.m 文件里,設(shè)定 baseUrl 以及 cdnUrl 等參數(shù)。
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
YTKNetworkConfig *config = [YTKNetworkConfig sharedConfig];
config.baseUrl = @"http://yuantiku.com";
config.cdnUrl = @"http://fen.bi";
}
如果我們需要新建一個注冊的請求,則需要創(chuàng)建一個繼承于YTKRequest的注冊接口的類RegisterApi,并將針對該請求參數(shù)配置好:
// RegisterApi.h
#import "YTKRequest.h"
@interface RegisterApi : YTKRequest
- (id)initWithUsername:(NSString *)username password:(NSString *)password;
@end
// RegisterApi.m
#import "RegisterApi.h"
@implementation RegisterApi {
NSString *_username;
NSString *_password;
}
//初始化的時候?qū)蓚€參數(shù)值傳入
- (id)initWithUsername:(NSString *)username password:(NSString *)password {
self = [super init];
if (self) {
_username = username;
_password = password;
}
return self;
}
//需要和baseUrl拼接的地址
- (NSString *)requestUrl {
// “ http://www.yuantiku.com ” 在 YTKNetworkConfig 中設(shè)置,這里只填除去域名剩余的網(wǎng)址信息
return @"/iphone/register";
}
//請求方法,某人是GET
- (YTKRequestMethod)requestMethod {
return YTKRequestMethodPOST;
}
//請求體
- (id)requestArgument {
return @{
@"username": _username,
@"password": _password
};
}
@end
單個請求的發(fā)起
還是剛才的注冊 API,在實例化以后,直接調(diào)用startWithCompletionBlockWithSuccess:failure 方法(或start方法)就可以發(fā)起它:
RegisterApi *api = [[RegisterApi alloc] initWithUsername:username password:password];
[api startWithCompletionBlockWithSuccess:^(YTKBaseRequest *request) {
// 你可以直接在這里使用 self
NSLog(@"succeed");
} failure:^(YTKBaseRequest *request) {
// 你可以直接在這里使用 self
NSLog(@"failed");
}];
六.源碼解析
一.封裝請求,調(diào)用 start 方法
startWithCompletionBlockWithSuccess:failure
來看一下YTKNetwork做了什么:
//YTKBaseRequest.m
//傳入成功和失敗的block,并保存起來
- (void)startWithCompletionBlockWithSuccess:(YTKRequestCompletionBlock)success
failure:(YTKRequestCompletionBlock)failure {
//保存成功和失敗的回調(diào)block,便于將來調(diào)用
[self setCompletionBlockWithSuccess:success failure:failure];
//發(fā)起請求
[self start];
}
//保存成功和失敗的block
- (void)setCompletionBlockWithSuccess:(YTKRequestCompletionBlock)success
failure:(YTKRequestCompletionBlock)failure {
self.successCompletionBlock = success;
self.failureCompletionBlock = failure;
}
當(dāng)保存完成功和失敗的 bloc k以后,調(diào)用 start 方法,于是來到了 YTKRequest 類(注意,雖然 YTKBaseRequest 也實現(xiàn)了 start 方法,但是由于 YTKRequest 類是它的子類并也實現(xiàn)了 start 方法,所以這里最先走的是 YTKRequest 類的 start 方法):
- (void)start {
//如果忽略緩存
//ignoreCache屬性是用戶手動設(shè)置的,如果用戶強(qiáng)制忽略緩存,則無論是否緩存是否存在,直接發(fā)送請求。
if (self.ignoreCache) {
//調(diào)用無緩存請求方法
[self startWithoutCache];
return;
}
//如果存在下載未完成的文件(不緩存下載請求)
//resumableDownloadPath是斷點下載路徑,如果該路徑不為空,說明有未完成的下載任務(wù),則直接發(fā)送請求繼續(xù)下載。
if (self.resumableDownloadPath) {
//調(diào)用無緩存請求方法
[self startWithoutCache];
return;
}
//獲取緩存失敗
//loadCacheWithError:方法驗證了加載緩存是否成功的方法(返回值為YES,說明可以加載緩存;反之亦然
if (![self loadCacheWithError:nil]) {
//調(diào)用無緩存請求方法
[self startWithoutCache];
return;
}
//到這里一定是有緩存
_dataFromCache = YES;
//開啟主線程異步方法
dispatch_async(dispatch_get_main_queue(), ^{
//緩存處理
[self requestCompletePreprocessor];
//用戶可以在這里進(jìn)行真正的回調(diào)前操作
[self requestCompleteFilter];
//執(zhí)行回調(diào)
YTKRequest *strongSelf = self;
//請求完成后的代理回調(diào)
[strongSelf.delegate requestFinished:strongSelf];
//請求成功的block
if (strongSelf.successCompletionBlock) {
strongSelf.successCompletionBlock(strongSelf);
}
//手動把成功和失敗block都置為nil,避免循環(huán)引用
[strongSelf clearCompletionBlock];
});
}
二.判斷有緩存情況還是無緩存情況
loadCacheWithError
驗證加載緩存是否成功的方法(返回值為YES,說明可以加載緩存;反之亦然),看一下具體實現(xiàn):
- (BOOL)loadCacheWithError:(NSError * _Nullable __autoreleasing *)error {
//緩存時間小于0,緩存不可用,返回NO(緩存時間默認(rèn)為-1,需要用戶手動設(shè)置,單位是秒)
if ([self cacheTimeInSeconds] < 0) {
if (error) {
*error = [NSError errorWithDomain:YTKRequestCacheErrorDomain code:YTKRequestCacheErrorInvalidCacheTime userInfo:@{ NSLocalizedDescriptionKey:@"Invalid cache time"}];
}
return NO;
}
//是否有緩存的元數(shù)據(jù),如果沒有,返回錯誤
//元數(shù)據(jù)是指數(shù)據(jù)的數(shù)據(jù),在這里描述了緩存數(shù)據(jù)本身的一些特征:包括版本號,緩存時間,敏感信息等等
if (![self loadCacheMetadata]) {
if (error) {
*error = [NSError errorWithDomain:YTKRequestCacheErrorDomain code:YTKRequestCacheErrorInvalidMetadata userInfo:@{ NSLocalizedDescriptionKey:@"Invalid metadata. Cache may not exist"}];
}
return NO;
}
//檢查緩存是否可用,如果不可用,返回NO
if (![self validateCacheWithError:error]) {
return NO;
}
//檢查緩存是否可以加載,如果不能加載,返回NO
if (![self loadCacheData]) {
if (error) {
*error = [NSError errorWithDomain:YTKRequestCacheErrorDomain code:YTKRequestCacheErrorInvalidCacheData userInfo:@{ NSLocalizedDescriptionKey:@"Invalid cache data"}];
}
return NO;
}
//其他情況表示能夠加載緩存
return YES;
}
loadCacheMetadata
我們來看一下上面關(guān)于緩存的元數(shù)據(jù)的獲取方法。
- (BOOL)loadCacheMetadata {
//獲取緩存元數(shù)據(jù)路徑
NSString *path = [self cacheMetadataFilePath];
NSFileManager * fileManager = [NSFileManager defaultManager];
//使用try catch捕獲異常避免崩潰
if ([fileManager fileExistsAtPath:path isDirectory:nil]) {
@try {
_cacheMetadata = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
return YES;
} @catch (NSException *exception) {
YTKLog(@"Load cache metadata failed, reason = %@", exception.reason);
return NO;
}
}
return NO;
}
其中 [self cacheMetadataFilePath] 緩存元數(shù)據(jù)路徑獲取。
緩存元數(shù)據(jù)路徑由根路徑加緩存元數(shù)據(jù)子路徑構(gòu)成。
- 根路徑:默認(rèn)為 library 路徑拼接 "LazyRequestCache" 。如果遵循 YTKCacheDirPathFilterProtocol 協(xié)議實現(xiàn)
- (NSString *)filterCacheDirPath:(NSString *)originPath withRequest:(YTKBaseRequest *)request;方法可更改跟路徑。根據(jù)路徑查詢目錄,如果目錄不存在則創(chuàng)建。 - 緩存元數(shù)據(jù)子路徑: 獲取
requestMethod、baseUrl、requestUrl、argument組成requestInfo,加密后形成緩存文件名。再加上.metadata形成緩存元數(shù)據(jù)子路徑。
cacheMetadata(YTKCacheMetadata)
它描述的是緩存的版本號,敏感信息,創(chuàng)建時間,app版本等信息,并支持序列化處理,可以保存在磁盤里。
因此,loadCacheMetadata 方法的目的是將之前被序列化保存的緩存元數(shù)據(jù)信息反序列化,賦給自身的cacheMetadata 屬性上。
//YTKRequest.m
@interface YTKCacheMetadata : NSObject<NSSecureCoding>
@property (nonatomic, assign) long long version;
@property (nonatomic, strong) NSString *sensitiveDataString;
@property (nonatomic, assign) NSStringEncoding stringEncoding;
@property (nonatomic, strong) NSDate *creationDate;
@property (nonatomic, strong) NSString *appVersionString;
@end
validateCacheWithError
逐一驗證元數(shù)據(jù)里的各項信息是否符合要求。
- (BOOL)validateCacheWithError:(NSError * _Nullable __autoreleasing *)error {
// 是否大于過期時間
NSDate *creationDate = self.cacheMetadata.creationDate;
NSTimeInterval duration = -[creationDate timeIntervalSinceNow];
//[self cacheTimeInSeconds] 設(shè)定緩存有效時間
if (duration < 0 || duration > [self cacheTimeInSeconds]) {
if (error) {
*error = [NSError errorWithDomain:YTKRequestCacheErrorDomain code:YTKRequestCacheErrorExpired userInfo:@{ NSLocalizedDescriptionKey:@"Cache expired"}];
}
return NO;
}
// 緩存的版本號是否符合
long long cacheVersionFileContent = self.cacheMetadata.version;
if (cacheVersionFileContent != [self cacheVersion]) {
if (error) {
*error = [NSError errorWithDomain:YTKRequestCacheErrorDomain code:YTKRequestCacheErrorVersionMismatch userInfo:@{ NSLocalizedDescriptionKey:@"Cache version mismatch"}];
}
return NO;
}
// 敏感信息是否符合 (元數(shù)據(jù)中的敏感信息 與 請求中設(shè)定的敏感信息 比對)
NSString *sensitiveDataString = self.cacheMetadata.sensitiveDataString;
NSString *currentSensitiveDataString = ((NSObject *)[self cacheSensitiveData]).description;
if (sensitiveDataString || currentSensitiveDataString) {
// If one of the strings is nil, short-circuit evaluation will trigger
if (sensitiveDataString.length != currentSensitiveDataString.length || ![sensitiveDataString isEqualToString:currentSensitiveDataString]) {
if (error) {
*error = [NSError errorWithDomain:YTKRequestCacheErrorDomain code:YTKRequestCacheErrorSensitiveDataMismatch userInfo:@{ NSLocalizedDescriptionKey:@"Cache sensitive data mismatch"}];
}
return NO;
}
}
// app的版本是否符合
NSString *appVersionString = self.cacheMetadata.appVersionString;
NSString *currentAppVersionString = [YTKNetworkUtils appVersionString];
if (appVersionString || currentAppVersionString) {
if (appVersionString.length != currentAppVersionString.length || ![appVersionString isEqualToString:currentAppVersionString]) {
if (error) {
*error = [NSError errorWithDomain:YTKRequestCacheErrorDomain code:YTKRequestCacheErrorAppVersionMismatch userInfo:@{ NSLocalizedDescriptionKey:@"App version mismatch"}];
}
return NO;
}
}
return YES;
}
loadCacheData
- (BOOL)loadCacheData {
// 獲取緩存路徑
NSString *path = [self cacheFilePath];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error = nil;
// 如果緩存目錄存在
if ([fileManager fileExistsAtPath:path isDirectory:nil]) {
NSData *data = [NSData dataWithContentsOfFile:path];
_cacheData = data;
_cacheString = [[NSString alloc] initWithData:_cacheData encoding:self.cacheMetadata.stringEncoding];
// 根據(jù)數(shù)據(jù)類型對緩存數(shù)據(jù)進(jìn)行處理
switch (self.responseSerializerType) {
case YTKResponseSerializerTypeHTTP:
// Do nothing.
return YES;
case YTKResponseSerializerTypeJSON:
_cacheJSON = [NSJSONSerialization JSONObjectWithData:_cacheData options:(NSJSONReadingOptions)0 error:&error];
return error == nil;
case YTKResponseSerializerTypeXMLParser:
_cacheXML = [[NSXMLParser alloc] initWithData:_cacheData];
return YES;
}
}
return NO;
}
dataFromCache
當(dāng)確認(rèn)緩存可以成功取出后,手動設(shè)置 dataFromCache 屬性為 YES,說明當(dāng)前的請求結(jié)果是來自于緩存,而沒有通過網(wǎng)絡(luò)請求。
requestCompletePreprocessor
- (void)requestCompletePreprocessor {
[super requestCompletePreprocessor];
// 是否異步將responseData寫入緩存(寫入緩存的任務(wù)放在專門的隊列ytkrequest_cache_writing_queue進(jìn)行)
if (self.writeCacheAsynchronously) {
dispatch_async(ytkrequest_cache_writing_queue(), ^{
// 保存響應(yīng)數(shù)據(jù)到緩存
[self saveResponseDataToCacheFile:[super responseData]];
});
} else {
// 保存響應(yīng)數(shù)據(jù)到緩存
[self saveResponseDataToCacheFile:[super responseData]];
}
}
- (void)saveResponseDataToCacheFile:(NSData *)data {
// 設(shè)置緩存時間大于0 且 此數(shù)據(jù)之前不是緩存數(shù)據(jù)
if ([self cacheTimeInSeconds] > 0 && ![self isDataFromCache]) {
// 數(shù)據(jù)額非空
if (data != nil) {
@try {
// 總是覆蓋舊數(shù)據(jù)
[data writeToFile:[self cacheFilePath] atomically:YES];
// 設(shè)置緩存元數(shù)據(jù)
YTKCacheMetadata *metadata = [[YTKCacheMetadata alloc] init];
metadata.version = [self cacheVersion];
metadata.sensitiveDataString = ((NSObject *)[self cacheSensitiveData]).description;
metadata.stringEncoding = [YTKNetworkUtils stringEncodingWithRequest:self];
metadata.creationDate = [NSDate date];
metadata.appVersionString = [YTKNetworkUtils appVersionString];
// 儲存緩存元數(shù)據(jù)
[NSKeyedArchiver archiveRootObject:metadata toFile:[self cacheMetadataFilePath]];
} @catch (NSException *exception) {
YTKLog(@"Save cache failed, reason = %@", exception.reason);
}
}
}
}
requestCompleteFilter
- (void)requestCompleteFilter {
}
可以看到此方法內(nèi)容為空。用戶可以在這里做一些回調(diào)前的處理。
三.緩存情況下
//到這里一定是有緩存
_dataFromCache = YES;
//開啟主線程異步方法
dispatch_async(dispatch_get_main_queue(), ^{
//緩存處理
[self requestCompletePreprocessor];
//用戶可以在這里進(jìn)行真正的回調(diào)前操作
[self requestCompleteFilter];
//執(zhí)行回調(diào)
YTKRequest *strongSelf = self;
//請求完成后的代理回調(diào)
[strongSelf.delegate requestFinished:strongSelf];
//請求成功的block
if (strongSelf.successCompletionBlock) {
strongSelf.successCompletionBlock(strongSelf);
}
//手動把成功和失敗block都置為nil,避免循環(huán)引用
[strongSelf clearCompletionBlock];
});
在有緩存的情況下,直接利用緩存執(zhí)行代理和 block 的回調(diào)
四.無緩存情況下
執(zhí)行無緩存請求。
[self startWithoutCache];
startWithoutCache
- (void)startWithoutCache {
// 清除本類中全局變量
[self clearCacheVariables];
//調(diào)用父類的 start 方法
[super start];
}
- (void)clearCacheVariables {
_cacheData = nil;
_cacheXML = nil;
_cacheJSON = nil;
_cacheString = nil;
_cacheMetadata = nil;
_dataFromCache = NO;
}
YTKBaseRequest start 方法
- (void)start {
// 告訴Accessories即將回調(diào)了(其實是即將發(fā)起請求)
[self toggleAccessoriesWillStartCallBack];
// Agent添加請求
[[YTKNetworkAgent sharedAgent] addRequest:self];
}
- (void)toggleAccessoriesWillStartCallBack {
// 遵循YTKRequestAccessory代理的類執(zhí)行start方法
for (id<YTKRequestAccessory> accessory in self.requestAccessories) {
if ([accessory respondsToSelector:@selector(requestWillStart:)]) {
[accessory requestWillStart:self];
}
}
}
addRequest
- (void)addRequest:(YTKBaseRequest *)request {
// 斷言request不為空
NSParameterAssert(request != nil);
// 定義error默認(rèn)為空
NSError * __autoreleasing requestSerializationError = nil;
// 獲取自定義request,默認(rèn)為空 如果自定義子類中中實現(xiàn)buildCustomUrlRequest方法則獲取方法中的request
NSURLRequest *customUrlRequest= [request buildCustomUrlRequest];
// 判斷自定義request是否存在
if (customUrlRequest) {
__block NSURLSessionDataTask *dataTask = nil;
// 調(diào)用afn中網(wǎng)絡(luò)請求方法
dataTask = [_manager dataTaskWithRequest:customUrlRequest completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
// 使用handleRequestResult處理afn返回結(jié)果
[self handleRequestResult:dataTask responseObject:responseObject error:error];
}];
request.requestTask = dataTask;
} else {
// 如果自定義request不存在,使用傳入的request(即baseRequest類型的具體request)
request.requestTask = [self sessionTaskForRequest:request error:&requestSerializationError];
}
// 如果請求序列化錯誤
if (requestSerializationError) {
// 處理錯誤
[self requestDidFailWithRequest:request error:requestSerializationError];
return;
}
// 斷言
NSAssert(request.requestTask != nil, @"requestTask should not be nil");
// ios8 以后可以設(shè)定請求優(yōu)先級
if ([request.requestTask respondsToSelector:@selector(priority)]) {
switch (request.requestPriority) {
case YTKRequestPriorityHigh:
request.requestTask.priority = NSURLSessionTaskPriorityHigh;
break;
case YTKRequestPriorityLow:
request.requestTask.priority = NSURLSessionTaskPriorityLow;
break;
case YTKRequestPriorityDefault:
/*!!fall through*/
default:
request.requestTask.priority = NSURLSessionTaskPriorityDefault;
break;
}
}
YTKLog(@"Add request: %@", NSStringFromClass([request class]));
//請求備份
[self addRequestToRecord:request];
// 開始請求
[request.requestTask resume];
}
此方法主要分三個部分:
- 第一部分是獲取當(dāng)前請求對應(yīng)的task并賦給request的requestTask屬性
- 第二部分是把request放入專門用來保存請求的字典中
- 第三部分是啟動task
sessionTaskForRequest: error :
- (NSURLSessionTask *)sessionTaskForRequest:(YTKBaseRequest *)request error:(NSError * _Nullable __autoreleasing *)error {
// 獲取請求方法類型
YTKRequestMethod method = [request requestMethod];
// 獲取url
NSString *url = [self buildRequestUrl:request];
// 獲取請求參數(shù)
id param = request.requestArgument;
AFConstructingBlock constructingBlock = [request constructingBodyBlock];
// 獲取request serializer
AFHTTPRequestSerializer *requestSerializer = [self requestSerializerForRequest:request];
// 根據(jù)不同請求方法類型返回task
switch (method) {
case YTKRequestMethodGET:
// 如果斷點下載
if (request.resumableDownloadPath) {
// 返回下載任務(wù)
return [self downloadTaskWithDownloadPath:request.resumableDownloadPath requestSerializer:requestSerializer URLString:url parameters:param progress:request.resumableDownloadProgressBlock error:error];
} else {
// 返回普通get任務(wù)
return [self dataTaskWithHTTPMethod:@"GET" requestSerializer:requestSerializer URLString:url parameters:param error:error];
}
case YTKRequestMethodPOST:
//返回post任務(wù)
return [self dataTaskWithHTTPMethod:@"POST" requestSerializer:requestSerializer URLString:url parameters:param constructingBodyWithBlock:constructingBlock error:error];
case YTKRequestMethodHEAD:
return [self dataTaskWithHTTPMethod:@"HEAD" requestSerializer:requestSerializer URLString:url parameters:param error:error];
case YTKRequestMethodPUT:
return [self dataTaskWithHTTPMethod:@"PUT" requestSerializer:requestSerializer URLString:url parameters:param error:error];
case YTKRequestMethodDELETE:
return [self dataTaskWithHTTPMethod:@"DELETE" requestSerializer:requestSerializer URLString:url parameters:param error:error];
case YTKRequestMethodPATCH:
return [self dataTaskWithHTTPMethod:@"PATCH" requestSerializer:requestSerializer URLString:url parameters:param error:error];
}
}
五. NSURLSessionTask 參數(shù)配置
URL處理
//YTKNetworkAgent.m
- (NSURLSessionTask *)sessionTaskForRequest:(YTKBaseRequest *)request error:(NSError * _Nullable __autoreleasing *)error {
...
NSString *url = [self buildRequestUrl:request];
...
}
- (NSString *)buildRequestUrl:(YTKBaseRequest *)request {
// 斷言
NSParameterAssert(request != nil);
//獲取子類中自定義requesturl
NSString *detailUrl = [request requestUrl];
NSURL *temp = [NSURL URLWithString:detailUrl];
// 存在host和scheme的url立即返回正確
if (temp && temp.host && temp.scheme) {
return detailUrl;
}
// 如果遵循YTKUrlFilterProtocol 重寫filterUrl方法 過濾url
NSArray *filters = [_config urlFilters];
for (id<YTKUrlFilterProtocol> f in filters) {
detailUrl = [f filterUrl:detailUrl withRequest:request];
}
NSString *baseUrl;
if ([request useCDN]) {
// 如果使用CDN,在當(dāng)前請求沒有配置CDN地址的情況下,返回全局配置的CDN
if ([request cdnUrl].length > 0) {
baseUrl = [request cdnUrl];
} else {
baseUrl = [_config cdnUrl];
}
} else {
// 如果使用baseUrl,在當(dāng)前請求沒有配置baseUrl,返回全局配置的baseUrl
if ([request baseUrl].length > 0) {
baseUrl = [request baseUrl];
} else {
baseUrl = [_config baseUrl];
}
}
// 如果末尾沒有/,則在末尾添加一個/
NSURL *url = [NSURL URLWithString:baseUrl];
if (baseUrl.length > 0 && ![baseUrl hasSuffix:@"/"]) {
url = [url URLByAppendingPathComponent:@""];
}
// 拼接baseUrl和detailUrl并返回完整url
return [NSURL URLWithString:detailUrl relativeToURL:url].absoluteString;
}
requestSerializerForRequest
- (AFHTTPRequestSerializer *)requestSerializerForRequest:(YTKBaseRequest *)request {
AFHTTPRequestSerializer *requestSerializer = nil;
// 判斷請求的序列化類型是HTTP還是JSON (可通過requestSerializerType手動設(shè)定)
if (request.requestSerializerType == YTKRequestSerializerTypeHTTP) {
// 創(chuàng)建HTTP類型
requestSerializer = [AFHTTPRequestSerializer serializer];
} else if (request.requestSerializerType == YTKRequestSerializerTypeJSON) {
// 創(chuàng)建JSON類型
requestSerializer = [AFJSONRequestSerializer serializer];
}
// 超時時間 默認(rèn)60 可手動設(shè)置
requestSerializer.timeoutInterval = [request requestTimeoutInterval];
// 是否允許使用蜂窩移動數(shù)據(jù)(4g)默認(rèn)可以 可手動設(shè)置
requestSerializer.allowsCellularAccess = [request allowsCellularAccess];
// 如果當(dāng)前請求需要服務(wù)器賬號和密碼
NSArray<NSString *> *authorizationHeaderFieldArray = [request requestAuthorizationHeaderFieldArray];
if (authorizationHeaderFieldArray != nil) {
[requestSerializer setAuthorizationHeaderFieldWithUsername:authorizationHeaderFieldArray.firstObject
password:authorizationHeaderFieldArray.lastObject];
}
// 如果當(dāng)前請求需要自定義 HTTPHeaderField
NSDictionary<NSString *, NSString *> *headerFieldValueDictionary = [request requestHeaderFieldValueDictionary];
if (headerFieldValueDictionary != nil) {
for (NSString *httpHeaderField in headerFieldValueDictionary.allKeys) {
NSString *value = headerFieldValueDictionary[httpHeaderField];
[requestSerializer setValue:value forHTTPHeaderField:httpHeaderField];
}
}
return requestSerializer;
}
六.開始請求
獲取 task
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
requestSerializer:(AFHTTPRequestSerializer *)requestSerializer
URLString:(NSString *)URLString
parameters:(id)parameters
constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
error:(NSError * _Nullable __autoreleasing *)error {
NSMutableURLRequest *request = nil;
// 根據(jù)有無Block使用AFN中的AFHTTPRequestSerializer創(chuàng)建request
if (block) {
request = [requestSerializer multipartFormRequestWithMethod:method URLString:URLString parameters:parameters constructingBodyWithBlock:block error:error];
} else {
request = [requestSerializer requestWithMethod:method URLString:URLString parameters:parameters error:error];
}
// 根據(jù)request創(chuàng)建dataTask
__block NSURLSessionDataTask *dataTask = nil;
dataTask = [_manager dataTaskWithRequest:request
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *_error) {
// 響應(yīng)的統(tǒng)一處理
[self handleRequestResult:dataTask responseObject:responseObject error:_error];
}];
return dataTask;
}
啟動task
[request.requestTask resume];
七.處理回調(diào)
handleRequestResult
響應(yīng)的統(tǒng)一處理內(nèi)部操作
- (void)handleRequestResult:(NSURLSessionTask *)task responseObject:(id)responseObject error:(NSError *)error {
// 根據(jù)task獲取request
Lock();
YTKBaseRequest *request = _requestsRecord[@(task.taskIdentifier)];
Unlock();
// When the request is cancelled and removed from records, the underlying
// AFNetworking failure callback will still kicks in, resulting in a nil `request`.
//
// Here we choose to completely ignore cancelled tasks. Neither success or failure
// callback will be called.
if (!request) {
return;
}
YTKLog(@"Finished Request: %@", NSStringFromClass([request class]));
NSError * __autoreleasing serializationError = nil;
NSError * __autoreleasing validationError = nil;
NSError *requestError = nil;
BOOL succeed = NO;
// 獲取request對應(yīng)的response
request.responseObject = responseObject;
if ([request.responseObject isKindOfClass:[NSData class]]) {
// 獲取 responseData
request.responseData = responseObject;
// 獲取responseString
request.responseString = [[NSString alloc] initWithData:responseObject encoding:[YTKNetworkUtils stringEncodingWithRequest:request]];
// 根據(jù)返回的響應(yīng)的序列化的類型來得到對應(yīng)類型的響應(yīng)
switch (request.responseSerializerType) {
case YTKResponseSerializerTypeHTTP:
// Default serializer. Do nothing.
break;
case YTKResponseSerializerTypeJSON:
request.responseObject = [self.jsonResponseSerializer responseObjectForResponse:task.response data:request.responseData error:&serializationError];
request.responseJSONObject = request.responseObject;
break;
case YTKResponseSerializerTypeXMLParser:
request.responseObject = [self.xmlParserResponseSerialzier responseObjectForResponse:task.response data:request.responseData error:&serializationError];
break;
}
}
// 判斷是否有錯誤,將錯誤對象賦值給requestError,改變succeed的布爾值。目的是根據(jù)succeed的值來判斷到底是進(jìn)行成功的回調(diào)還是失敗的回調(diào)
if (error) {
// 如果該方法傳入的error不為nil
succeed = NO;
requestError = error;
} else if (serializationError) {
// 如果序列化失敗了
succeed = NO;
requestError = serializationError;
} else {
// 即使沒有error而且序列化通過,也要驗證request是否有效
succeed = [self validateResult:request error:&validationError];
requestError = validationError;
}
// 根據(jù)succeed的布爾值來調(diào)用相應(yīng)的處理
if (succeed) {
// 請求成功的處理
[self requestDidSucceedWithRequest:request];
} else {
// 請求失敗的處理
[self requestDidFailWithRequest:request error:requestError];
}
// 回調(diào)完成的處理
dispatch_async(dispatch_get_main_queue(), ^{
// 在字典里移除當(dāng)前request
[self removeRequestFromRecord:request];
// 清除所有block
[request clearCompletionBlock];
});
}
簡單講解一下上面的代碼:
- 前面以 task 的 identifier 為 key,將 request 存入字典,這里根據(jù) task 取出 request。
- 然后將獲得的responseObject進(jìn)行處理,將處理后獲得的responseObject,responseData和responseString賦值給當(dāng)前的請求實例request。
- 再根據(jù)這些值的獲取情況來判斷最終回調(diào)的成?。ǜ淖僺ucceed的值)。
- 最后根據(jù)succeed的值來進(jìn)行成功和失敗的回調(diào)。
validateResult
驗證返回的json數(shù)據(jù)是否有效
- (BOOL)validateResult:(YTKBaseRequest *)request error:(NSError * _Nullable __autoreleasing *)error {
// 判斷statusCode是否在200~299之間,如果是則返回狀態(tài)有效
BOOL result = [request statusCodeValidator];
if (!result) {
// 如果狀態(tài)碼無效,傳入error地址存在
if (error) {
// 創(chuàng)建NSError對象賦值給error響應(yīng)地址
*error = [NSError errorWithDomain:YTKRequestValidationErrorDomain code:YTKRequestValidationErrorInvalidStatusCode userInfo:@{NSLocalizedDescriptionKey:@"Invalid status code"}];
}
// 返回result
return result;
}
// 狀態(tài)碼有效的情況下判斷json是否有效
// 去返回json
id json = [request responseJSONObject];
// 取設(shè)定的json數(shù)據(jù)格式,可手動設(shè)置
id validator = [request jsonValidator];
// 如果同時存在
if (json && validator) {
// 判斷json是否符合設(shè)定的jsonValidator
result = [YTKNetworkUtils validateJSON:json withValidator:validator];
if (!result) {
if (error) {
*error = [NSError errorWithDomain:YTKRequestValidationErrorDomain code:YTKRequestValidationErrorInvalidJSONFormat userInfo:@{NSLocalizedDescriptionKey:@"Invalid JSON format"}];
}
return result;
}
}
return YES;
}
在這里,首先,用statusCodeValidator方法判斷響應(yīng)的code是否在正確的范圍,然后再判斷json的有效性。
validateJSON
判斷目標(biāo)字典對象是否滿足于設(shè)置字典模型的key一致時,value類型與自定義類型格式相同
+ (BOOL)validateJSON:(id)json withValidator:(id)jsonValidator {
// 判斷是否同為字典類型
if ([json isKindOfClass:[NSDictionary class]] &&
[jsonValidator isKindOfClass:[NSDictionary class]]) {
// 轉(zhuǎn)為字典類型
NSDictionary * dict = json;
NSDictionary * validator = jsonValidator;
BOOL result = YES;
// 實例遍歷類型對象
NSEnumerator * enumerator = [validator keyEnumerator];
NSString * key;
// 使用while遍歷validator的key數(shù)組,直到為空
while ((key = [enumerator nextObject]) != nil) {
// 根據(jù)key取dict字典value,和validator字典中定義類型
id value = dict[key];
id format = validator[key];
// 如果vlue仍為字典或者數(shù)組,遞歸
if ([value isKindOfClass:[NSDictionary class]]
|| [value isKindOfClass:[NSArray class]]) {
result = [self validateJSON:value withValidator:format];
if (!result) {
break;
}
} else {
// 如果value不為定義類型 且 value不為空類型
if ([value isKindOfClass:format] == NO &&
[value isKindOfClass:[NSNull class]] == NO) {
// 結(jié)果為NO
result = NO;
// 有一個結(jié)果為NO則全部為NO, 不需要再循環(huán),跳出
break;
}
}
}
// 返回結(jié)果
return result;
// 如果json和jsonValidator同為數(shù)組類型
} else if ([json isKindOfClass:[NSArray class]] &&
[jsonValidator isKindOfClass:[NSArray class]]) {
NSArray * validatorArray = (NSArray *)jsonValidator;
if (validatorArray.count > 0) {
// 目標(biāo)對象轉(zhuǎn)為數(shù)組
NSArray * array = json;
// 驗證對象數(shù)組取第一個字典為模型即可
NSDictionary * validator = jsonValidator[0];
// 目標(biāo)對象中的每一個格式 和 字典模型一致即可
for (id item in array) {
// 遞歸
BOOL result = [self validateJSON:item withValidator:validator];
if (!result) {
return NO;
}
}
}
return YES;
// 類型直接相同返回yes
} else if ([json isKindOfClass:jsonValidator]) {
return YES;
// 類型不同返回no
} else {
return NO;
}
}
requestDidSucceedWithRequest
請求成功的處理。
- (void)requestDidSucceedWithRequest:(YTKBaseRequest *)request {
// 加入手動創(chuàng)建的自動釋放池中,執(zhí)行完池中方法后相應(yīng)內(nèi)存直接釋放
// 響應(yīng)數(shù)據(jù)寫入文件緩存后相應(yīng)內(nèi)存直接釋放,最大限度降低內(nèi)存
@autoreleasepool {
// 寫入緩存
[request requestCompletePreprocessor];
}
// 異步回到主隊列
dispatch_async(dispatch_get_main_queue(), ^{
// 告訴Accessories請求就要結(jié)束了
[request toggleAccessoriesWillStopCallBack];
// 在最后的回調(diào)之前可以做的處理,用戶可以自定義
[request requestCompleteFilter];
// 如果有代理,則調(diào)用成功的代理
if (request.delegate != nil) {
[request.delegate requestFinished:request];
}
// 如果傳入了成功的block,則調(diào)用
if (request.successCompletionBlock) {
request.successCompletionBlock(request);
}
// 告訴Accessories請求已經(jīng)結(jié)束了
[request toggleAccessoriesDidStopCallBack];
});
}
requestDidFailWithRequest
請求失敗處理。
- (void)requestDidFailWithRequest:(YTKBaseRequest *)request error:(NSError *)error {
// 賦值error
request.error = error;
YTKLog(@"Request %@ failed, status code = %ld, error = %@",
NSStringFromClass([request class]), (long)request.responseStatusCode, error.localizedDescription);
// 儲存未完成的下載數(shù)據(jù),存入resumableDownloadPath,等待斷點續(xù)傳
NSData *incompleteDownloadData = error.userInfo[NSURLSessionDownloadTaskResumeData];
if (incompleteDownloadData) {
[incompleteDownloadData writeToURL:[self incompleteDownloadTempPathForDownloadPath:request.resumableDownloadPath] atomically:YES];
}
// 下載失敗清除文件目錄中的文件并清空響應(yīng)
if ([request.responseObject isKindOfClass:[NSURL class]]) {
NSURL *url = request.responseObject;
if (url.isFileURL && [[NSFileManager defaultManager] fileExistsAtPath:url.path]) {
request.responseData = [NSData dataWithContentsOfURL:url];
request.responseString = [[NSString alloc] initWithData:request.responseData encoding:[YTKNetworkUtils stringEncodingWithRequest:request]];
[[NSFileManager defaultManager] removeItemAtURL:url error:nil];
}
request.responseObject = nil;
}
// 失敗預(yù)處理默認(rèn)是不做處理的,如需要需自定義
@autoreleasepool {
[request requestFailedPreprocessor];
}
dispatch_async(dispatch_get_main_queue(), ^{
// 告訴Accessories請求就要結(jié)束了
[request toggleAccessoriesWillStopCallBack];
// 在真正的回調(diào)之前做的處理,可自定義
[request requestFailedFilter];
// 如果有代理,就調(diào)用代理
if (request.delegate != nil) {
[request.delegate requestFailed:request];
}
// 如果傳入了失敗回調(diào)的block代碼,就調(diào)用block
if (request.failureCompletionBlock) {
request.failureCompletionBlock(request);
}
// 告訴Accessories請求已經(jīng)停止了
[request toggleAccessoriesDidStopCallBack];
});
}
在這個方法里,首先判斷了當(dāng)前任務(wù)是否為下載任務(wù),如果是,則儲存當(dāng)前已經(jīng)下載好的data到resumableDownloadPath里面。而如果下載任務(wù)失敗,則將其對應(yīng)的在本地保存的路徑上的文件清空。

八.取消請求
兩個取消方法:
//YTKNetworkAgent.h
/// 取消某個request
- (void)cancelRequest:(YTKBaseRequest *)request;
/// 取消所有添加的request
- (void)cancelAllRequests;
cancelRequest
取消某個request。
- (void)cancelRequest:(YTKBaseRequest *)request {
// 斷言request不為空
NSParameterAssert(request != nil);
// 如果下載過程中取消
if (request.resumableDownloadPath) {
NSURLSessionDownloadTask *requestTask = (NSURLSessionDownloadTask *)request.requestTask;
// 寫入resumableDownloadPath等待斷點續(xù)傳。方法內(nèi)部會調(diào)用cancel方法
[requestTask cancelByProducingResumeData:^(NSData *resumeData) {
NSURL *localUrl = [self incompleteDownloadTempPathForDownloadPath:request.resumableDownloadPath];
[resumeData writeToURL:localUrl atomically:YES];
}];
} else {
// 獲取request的task,并取消
[request.requestTask cancel];
}
// 從字典里移除當(dāng)前request
[self removeRequestFromRecord:request];
// 清理所有block
[request clearCompletionBlock];
}
- (void)removeRequestFromRecord:(YTKBaseRequest *)request {
// 加鎖
Lock();
// 移除字典中這組數(shù)據(jù)
[_requestsRecord removeObjectForKey:@(request.requestTask.taskIdentifier)];
YTKLog(@"Request queue size = %zd", [_requestsRecord count]);
Unlock();
}
cancelAllRequests
取消所有request。
- (void)cancelAllRequests {
// 加鎖。多個線程對同一個字典進(jìn)行操作的,都需要加鎖
Lock();
NSArray *allKeys = [_requestsRecord allKeys];
Unlock();
if (allKeys && allKeys.count > 0) {
// copy
NSArray *copiedKeys = [allKeys copy];
for (NSNumber *key in copiedKeys) {
Lock();
YTKBaseRequest *request = _requestsRecord[key];
Unlock();
// We are using non-recursive lock.
// Do not lock `stop`, otherwise deadlock may occur.
// stop每個請求
[request stop];
}
}
}
stop
- (void)stop {
// 告訴Accessories將要停止回調(diào)了
[self toggleAccessoriesWillStopCallBack];
// 清空代理
self.delegate = nil;
// 調(diào)用agent的取消某個request的方法
[[YTKNetworkAgent sharedAgent] cancelRequest:self];
// 告訴Accessories已經(jīng)停止回調(diào)完成了
[self toggleAccessoriesDidStopCallBack];
}
九.批量請求
初始化
- (instancetype)initWithRequestArray:(NSArray<YTKRequest *> *)requestArray {
self = [super init];
if (self) {
// 保存為屬性,使用copy即使requestArray改變_requestArray也不會變
_requestArray = [requestArray copy];
//批量請求完成的數(shù)量初始化為0
_finishedCount = 0;
//類型檢查,所有元素都必須為YTKRequest或的它的子類,否則強(qiáng)制初始化失敗
for (YTKRequest * req in _requestArray) {
if (![req isKindOfClass:[YTKRequest class]]) {
YTKLog(@"Error, request item must be YTKRequest instance.");
return nil;
}
}
}
return self;
}
初始化以后,我們就可以調(diào)用start方法來發(fā)起當(dāng)前YTKBatchRequest實例所管理的所有請求了。
- (void)start {
//如果batch里第一個請求已經(jīng)成功結(jié)束,則不能再start
if (_finishedCount > 0) {
YTKLog(@"Error! Batch request has already started.");
return;
}
//最開始設(shè)定失敗的request為nil
_failedRequest = nil;
//使用YTKBatchRequestAgent來管理當(dāng)前的批量請求
[[YTKBatchRequestAgent sharedAgent] addBatchRequest:self];
[self toggleAccessoriesWillStartCallBack];
//遍歷所有request,并開始請求
for (YTKRequest * req in _requestArray) {
req.delegate = self;
[req clearCompletionBlock];
[req start];
}
}
requestFinished
單個請求完成,如果全部完成則批量請求完成。
- (void)requestFinished:(YTKRequest *)request {
//某個request成功后,首先讓_finishedCount + 1
_finishedCount++;
//如果_finishedCount等于_requestArray的個數(shù),則判定當(dāng)前batch請求成功
if (_finishedCount == _requestArray.count) {
//調(diào)用即將結(jié)束的代理
[self toggleAccessoriesWillStopCallBack];
//調(diào)用請求成功的代理
if ([_delegate respondsToSelector:@selector(batchRequestFinished:)]) {
[_delegate batchRequestFinished:self];
}
//調(diào)用批量請求成功的block
if (_successCompletionBlock) {
_successCompletionBlock(self);
}
//清空成功和失敗的block
[self clearCompletionBlock];
//調(diào)用請求結(jié)束的代理
[self toggleAccessoriesDidStopCallBack];
//從YTKBatchRequestAgent里移除當(dāng)前的batch
[[YTKBatchRequestAgent sharedAgent] removeBatchRequest:self];
}
}
requestFailed
單個請求失敗即批量請求失敗,將失敗的請求保存到_failedRequest中
- (void)requestFailed:(YTKRequest *)request {
// 失敗請求保存在_failedRequest中
_failedRequest = request;
// 調(diào)用即將結(jié)束的代理
[self toggleAccessoriesWillStopCallBack];
// 停止batch里所有的請求
for (YTKRequest *req in _requestArray) {
[req stop];
}
// 調(diào)用批量請求失敗的代理
if ([_delegate respondsToSelector:@selector(batchRequestFailed:)]) {
[_delegate batchRequestFailed:self];
}
// 調(diào)用請求失敗的block
if (_failureCompletionBlock) {
_failureCompletionBlock(self);
}
// 清空成功和失敗的block
[self clearCompletionBlock];
// 調(diào)用請求結(jié)束的代理
[self toggleAccessoriesDidStopCallBack];
// 從YTKBatchRequestAgent里移除當(dāng)前的batch
[[YTKBatchRequestAgent sharedAgent] removeBatchRequest:self];
}
十.鏈?zhǔn)秸埱?/h4>
和批量請求類似,處理鏈?zhǔn)秸埱蟮念愂荵TKChainRequest,并且用YTKChainRequestAgent單例來管理YTKChainRequest的實例
init
初始化方法。
- (instancetype)init {
self = [super init];
if (self) {
// 下一個請求的index
_nextRequestIndex = 0;
// 保存鏈?zhǔn)秸埱蟮臄?shù)組
_requestArray = [NSMutableArray array];
// 保存回調(diào)的數(shù)組
_requestCallbackArray = [NSMutableArray array];
// 空回調(diào),用來填充用戶沒有定義的回調(diào)block
_emptyCallback = ^(YTKChainRequest *chainRequest, YTKBaseRequest *baseRequest) {
// do nothing
};
}
return self;
}
addRequest
添加request的接口。
- (void)addRequest:(YTKBaseRequest *)request callback:(YTKChainCallback)callback {
// 添加當(dāng)前請求進(jìn)入請求數(shù)組
[_requestArray addObject:request];
// 傳入回調(diào)Block時
if (callback != nil) {
// 回調(diào)block存入回調(diào)數(shù)組
[_requestCallbackArray addObject:callback];
} else {
// 之前之所以創(chuàng)建一個空的block回調(diào)是為了在用戶不傳入回調(diào)時對應(yīng)傳入創(chuàng)建的空回調(diào),保持添加的請求和回調(diào)個數(shù)一致一一對應(yīng)。
[_requestCallbackArray addObject:_emptyCallback];
}
}
start
請求的發(fā)起。
- (void)start {
// 如果第1個請求已經(jīng)結(jié)束,就不再重復(fù)start了。nextRequestIndex為0,請求重頭開始。
if (_nextRequestIndex > 0) {
YTKLog(@"Error! Chain request has already started.");
return;
}
// //如果請求隊列數(shù)組里面還有request,則取出并start
if ([_requestArray count] > 0) {
// 將要開始回調(diào)
[self toggleAccessoriesWillStartCallBack];
// 開始下一個請求
[self startNextRequest];
// 在YTKChainRequestAgent請求數(shù)組中加入當(dāng)前鏈?zhǔn)秸埱螅纯梢杂卸鄠€鏈?zhǔn)秸埱笸瑫r存在)
[[YTKChainRequestAgent sharedAgent] addChainRequest:self];
} else {
YTKLog(@"Error! Chain request array is empty.");
}
}
- (BOOL)startNextRequest {
// 判斷不超出數(shù)組
if (_nextRequestIndex < [_requestArray count]) {
// 取出request
YTKBaseRequest *request = _requestArray[_nextRequestIndex];
// index+1
_nextRequestIndex++;
// 指定代理
request.delegate = self;
// 清除block
[request clearCompletionBlock];
// 開始請求
[request start];
return YES;
} else {
return NO;
}
}
所以和批量請求不同的是,鏈?zhǔn)秸埱蟮恼埱箨犃惺强梢宰儎拥?,用戶可以無限制地添加請求。只要請求隊列里面有請求存在,則YTKChainRequest就會繼續(xù)發(fā)送它們。
成功回調(diào)
- (void)requestFinished:(YTKBaseRequest *)request {
// 獲取當(dāng)前回調(diào)
NSUInteger currentRequestIndex = _nextRequestIndex - 1;
YTKChainCallback callback = _requestCallbackArray[currentRequestIndex];
// 當(dāng)前請求回調(diào)
callback(self, request);
//如果沒有下一個請求了
if (![self startNextRequest]) {
// 鏈?zhǔn)秸埱髮⒁Y(jié)束
[self toggleAccessoriesWillStopCallBack];
// 鏈?zhǔn)秸埱蟠砘卣{(diào)
if ([_delegate respondsToSelector:@selector(chainRequestFinished:)]) {
[_delegate chainRequestFinished:self];
// 從YTKChainRequestAgent列表中移除當(dāng)前鏈?zhǔn)秸埱? [[YTKChainRequestAgent sharedAgent] removeChainRequest:self];
}
// 鏈?zhǔn)秸埱蠼Y(jié)束
[self toggleAccessoriesDidStopCallBack];
}
}
失敗回調(diào)
- (void)requestFailed:(YTKBaseRequest *)request {
//如果當(dāng)前 chain里的某個request失敗了,則判定當(dāng)前chain失敗。調(diào)用當(dāng)前chain失敗的回調(diào)
[self toggleAccessoriesWillStopCallBack];
if ([_delegate respondsToSelector:@selector(chainRequestFailed:failedBaseRequest:)]) {
[_delegate chainRequestFailed:self failedBaseRequest:request];
[[YTKChainRequestAgent sharedAgent] removeChainRequest:self];
}
[self toggleAccessoriesDidStopCallBack];
}
stop
- (void)stop {
//首先調(diào)用即將停止的callback
[self toggleAccessoriesWillStopCallBack];
//然后stop當(dāng)前的請求,再清空chain里所有的請求和回掉block
[self clearRequest];
//在YTKChainRequestAgent里移除當(dāng)前的chain
[[YTKChainRequestAgent sharedAgent] removeChainRequest:self];
//最后調(diào)用已經(jīng)結(jié)束的callback
[self toggleAccessoriesDidStopCallBack];
}
- (void)clearRequest {
//獲取當(dāng)前請求的index
NSUInteger currentRequestIndex = _nextRequestIndex - 1;
if (currentRequestIndex < [_requestArray count]) {
YTKBaseRequest *request = _requestArray[currentRequestIndex];
// 停止當(dāng)前請求
[request stop];
}
//清空請求數(shù)組
[_requestArray removeAllObjects];
//請求回調(diào)數(shù)組
[_requestCallbackArray removeAllObjects];
}