转载链接:http://www.jianshu.com/p/521a6437a0b6
参考上面链接才稍微看懂源码的,同时将原来的老版代码,换成新版的;
使用后,画出他们的调用关系,这样方便看懂,方便理解
还有其他好多理解文章;
猿题库 iOS 客户端 网络库封装
https://github.com/yuantiku/YTKNetwork
YTKNetwork 源码分析
https://github.com/subvin/YTKNetworkAnalysis
BANetworking
https://github.com/beyondabel/BANetworking
首先
关于网络层最先可能想到的是AFNetworking
,或者Swift中的Alamofire
,直接使用起来也特别的简单,但是稍复杂的项目如果直接使用就显得不够用了,首先第三方耦合不说,就光散落在各处的请求回调就难以后期维护,所以一般会有针对性的再次封装,往往初期可能业务相对简单,考虑的方面较少,后期业务增加可能需要对网络层进行重构,一个好的架构也一定是和业务层面紧密相连的,随业务的增长不断健壮的。
最近也是看了YTKNetwork
的源码和相关博客,站在前辈的肩膀上写下一些自己关于网络层的解读。
与业务层对接方式
常见的与业务层对接方式两种:
- 集约型:
最典型就属于上面说的AFNetworking
、Alamofire
,发起网络请求都集中在一个类上,请求回调通过Block、闭包实现的,Block、闭包回调有比较好的灵活性,可以方便的在任何位置发起请求,同时也可能是不好的地方,网络请求回调散落在各处,不便于维护。
下面是一个集约型的网络请求,大家使用集约型网络请求有没有遇到这么一个场景,请求回调后需要做比较多的处理,代码量多的时候,会再定义一个私有方法把代码写在里面,在Block中调用在私有方法。其实这样处理本质上和通过代理回调本质上是一样的。
[_manager GET:url parameters:param success:^(AFHTTPRequestOperation *operation, id responseObject) {
//The data processing, Rendering interface
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
- 离散型:
对应的是接下来的YTKNetwork
,离散型最大的特点就是一个网络请求对应一个单独的类,在这个类内部封装请求地址、方式、参数、校验和处理请求回来的数据,通常代理回调,需要跨层数据传递时也使用通知回调,比较集中,因为数据处理都放在内部处理了,返回数据的形式(模型化后的数据还是其他)不需要控制器关心,控制器只需要在代理返回的数据可以直接对渲染UI,让Controller更加轻量化。
发起请求
NSString *userId = @"1";
GetUserInfoApi *api = [[GetUserInfoApi alloc] initWithUserId:userId];
[api start];
api.delegate = self;
Delegate回调
- (void)requestFinished:(YTKBaseRequest *)request {
NSLog(@"----- succeed ---- %@", request.responseJSONObject);
//Rendering interface
}
- (void)requestFailed:(YTKBaseRequest *)request {
NSLog(@"failed");
}
YTKNetwork解析
首先看下YTKNetwork的类文件:
图解它们之间的调用关系,注意还是理顺关系,看懂这个图应该对源码的理解没有太多问题:
YTKBaseRequest
:YTKRequest的父类,定义了Request的相关属性,Block和Delegate。给对外接口默认的实现,以及公共逻辑。YTKRequest
:主要对缓存做处理,更新缓存、读取缓存、手动写入缓存,是否忽略缓存。这里采用归档形式缓存,请求方式、根路径、请求地址、请求参数、app版本号、敏感数据拼接再MD5作为缓存的文件名,保证唯一性。还提供设置缓存的保存时长,主要实现是通过获取缓存文件上次修改的时刻距离现在的时间和设置的缓存时长作比较,来判断是否真正发起请求,下面是发起请求的一些逻辑判断:
- (void)start {
if (self.ignoreCache) // 如果忽略缓存 --> 网络请求
{
[self startWithoutCache];
return;
}
// Do not cache download request.
if (self.resumableDownloadPath) // 不会缓存下载请求 --> 网络请求
{
[self startWithoutCache];
return;
}
if (![self loadCacheWithError:nil]) // 从缓存加载出错 --> 网络请求
{
[self startWithoutCache];
return;
}
_dataFromCache = YES; // 从缓存加载数据
dispatch_async(dispatch_get_main_queue(), ^{
[self requestCompletePreprocessor];
[self requestCompleteFilter];
YTKRequest *strongSelf = self;
[strongSelf.delegate requestFinished:strongSelf];
if (strongSelf.successCompletionBlock) {
strongSelf.successCompletionBlock(strongSelf);
}
[strongSelf clearCompletionBlock];
});
}
通过归档存储网络请求的数据:
- (void)saveResponseDataToCacheFile:(NSData *)data {
if ([self cacheTimeInSeconds] > 0 && ![self isDataFromCache]) {
if (data != nil) {
@try {
// New data will always overwrite old data.
[data writeToFile:[self cacheFilePath] atomically:YES];
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];
[NSKeyedArchiver archiveRootObject:metadata toFile:[self cacheMetadataFilePath]];
} @catch (NSException *exception) {
YTKLog(@"Save cache failed, reason = %@", exception.reason);
}
}
}
}
-
YTKNetworkAgent
:真正发起网络请求的类,在addRequest
方法里调用AFN的方法,这块可以方便的更换第三方库,还包括一些请求取消,插件的代理方法调用等,所有网络请求失败或者成功都会调用下面这个方法:
- (void)handleRequestResult:(NSURLSessionTask *)task responseObject:(id)responseObject error:(NSError *)error {
Lock();
YTKBaseRequest *request = _requestsRecord[@(task.taskIdentifier)]; // 从dic中获取request
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.responseObject = responseObject;
if ([request.responseObject isKindOfClass:[NSData class]]) {
request.responseData = responseObject;
request.responseString = [[NSString alloc] initWithData:responseObject encoding:[YTKNetworkUtils stringEncodingWithRequest:request]];
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;
}
}
if (error) {
succeed = NO;
requestError = error;
} else if (serializationError) {
succeed = NO;
requestError = serializationError;
} else {
succeed = [self validateResult:request error:&validationError];
requestError = validationError;
}
if (succeed) // 请求成功
{
[self requestDidSucceedWithRequest:request];
} else // 请求失败
{
[self requestDidFailWithRequest:request error:requestError];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self removeRequestFromRecord:request]; // 从dic中移除request
[request clearCompletionBlock]; // block置nil,可以打破循环引用;
});
}
YTKNetworkConfig
:配置请求根路径、DNS地址。YTKNetworkPrivate
:可以理解为一个工具类,拼接地址,提供加密方法,定义分类等。YTKBatchRequest
、YTKChainRequest
:这是YKTNetwork的两个高级用法,批量网络请求和链式的网络请求,相当于一个存放Request的容器,先定义下面属性,
YTKBatchRequest
finishedCount来记录批量请求的完成的个数:
@interface YTKBatchRequest()
@property (nonatomic) NSInteger finishedCount;
@end
每完成一个请求finishedCount++,直到finishedCount等于所有请求的个数时才回调成功。
#pragma mark - Network Request Delegate
- (void)requestFinished:(YTKRequest *)request {
_finishedCount++;
if (_finishedCount == _requestArray.count) {
[self toggleAccessoriesWillStopCallBack];
if ([_delegate respondsToSelector:@selector(batchRequestFinished:)]) {
[_delegate batchRequestFinished:self];
}
if (_successCompletionBlock) {
_successCompletionBlock(self);
}
[self clearCompletionBlock];
[self toggleAccessoriesDidStopCallBack];
[[YTKBatchRequestAgent sharedAgent] removeBatchRequest:self];
}
}
YTKChainRequest
给Request绑定一个Index
@interface YTKChainRequest()
@property (assign, nonatomic) NSUInteger nextRequestIndex;
@end
从requestArray数组中依次取出发起网络请求,同时nextRequestIndex++,只要一个请求失败则触发失败的回调:
- (void)start {
if (_nextRequestIndex > 0) {
YTKLog(@"Error! Chain request has already started.");
return;
}
if ([_requestArray count] > 0) {
[self toggleAccessoriesWillStartCallBack];
[self startNextRequest];
[[YTKChainRequestAgent sharedAgent] addChainRequest:self];
} else {
YTKLog(@"Error! Chain request array is empty.");
}
}
//下一个网络请求
- (BOOL)startNextRequest {
if (_nextRequestIndex < [_requestArray count]) {
YTKBaseRequest *request = _requestArray[_nextRequestIndex];
_nextRequestIndex++;
request.delegate = self;
[request clearCompletionBlock];
[request start];
return YES;
} else {
return NO;
}
}
最后
最近也是看得比写代码多,大都一些源码和博客,种类也比较泛,读懂作者的思路还是收获颇多,往往有时候会要上好几遍,每一遍都有些新的收获,贵在坚持了https://github.com/ShelinShelin/SourceCodeAnalyze。