iOS YTKNetwork源码解析

我们经常把数据请求和数据处理放到Controller中,导致对AFNetwork的依赖过高,实际上AFNetwork只是一个网络支撑平台,我们需要根据业务需求(加密request、缓存、批量请求等)对它进行二次封装,YTKNetwork做了比较好的封装。

YTKNetwork 的基本思想

YTKNetwork 的基本的思想是把每一个网络请求封装成对象。所以使用 YTKNetwork,你的每一个请求都需要继承 YTKRequest 类,通过覆盖父类的一些方法来构造指定的网络请求。

YTKNetwork架构

熟悉架构以后能更充分的理解其设计思想和原理

#import "YTKRequest.h"
#import "YTKBaseRequest.h"
#import "YTKNetworkAgent.h"
#import "YTKBatchRequest.h"
#import "YTKBatchRequestAgent.h"
#import "YTKChainRequest.h"
#import "YTKChainRequestAgent.h"
#import "YTKNetworkConfig.h"

YTKNetwork采用的设计模式是命令模式,这个模式的特点是将命令包裹成对象,传递给调用对象,调用对象再去寻找合适的能够执行命令的对象,把命令传给它来执行任务。

  • 命令模式的目的:
    在软件系统中,行为请求和行为实现者往往是紧耦合关系,如果需要对行为进行记录、终止、事务等操作,紧耦合就显得不是很合理。
  • 命令模式的优缺点
    优点:降低了耦合度,新的命令可以很容易添加到系统中。
    缺点:会导致系统产生很多命令类。

命令模式类图


iOS YTKNetwork源码解析_第1张图片
命令模式类图.png

在YTKNetwork中各个类分属如下:

  • invoker:YTKNetworkAgent,命令调用者。
  • Command:YTKBaseRequest,抽象命令,命令都在这个Command里面声明。
  • ConcreteCommand:YTKRequest,命令实现类。
  • Receiver:AFNetwork,命令执行者。
  • Client:ViewController/ViewModel,发起命令者。
使用

正如我们上面所说,YTKNetwork是命令模式,需要把请求request当做一个具体命令,所以需要创建一个继承于YTKRequest的命令类,

#import "YTKRequest.h"

@interface RegisterApi : YTKRequest

- (id)initWithUsername:(NSString *)username password:(NSString *)password;

- (NSString *)userId;

@end

@implementation RegisterApi {
    NSString *_username;
    NSString *_password;
}

- (id)initWithUsername:(NSString *)username password:(NSString *)password {
    self = [super init];
    if (self) {
        _username = username;
        _password = password;
    }
    return self;
}

- (NSString *)requestUrl {
    return @"/iphone/register";
}

- (YTKRequestMethod)requestMethod {
    return YTKRequestMethodPOST;
}

- (id)requestArgument {
    return @{
        @"username": _username,  
        @"password": _password
    };
}
@end

上面代码就是实现的具体命令类

  • requestUrl:请求需要拼接在baseUrl后面的接口url
  • requestMethod:请求类型YTKRequestMethodPOST/YTKRequestMethodGET
  • requestArgument:请求参数

这样就构成了一个基本的命令类。

源码解析

上面我们创建了一个具体命令类RegisterApi,官方给出的Demo是如下调用的

TestApi *testApi = [[TestApi alloc] init];
[testApi startWithCompletionBlockWithSuccess:^(__kindof YTKBaseRequest * _Nonnull request) {
    NSLog(@"成功---- %@",request.responseObject);
} failure:^(__kindof YTKBaseRequest * _Nonnull request) {
    NSLog(@"失败");
}];

需要调用startWithCompletionBlockWithSuccess来开始具体的请求,进入YTKBaseRequest看源码,这里可以分成两块儿,从缓存读取数据从网络请求数据

#import "YTKBaseRequest.h"
- (void)startWithCompletionBlockWithSuccess:(YTKRequestCompletionBlock)success
                                    failure:(YTKRequestCompletionBlock)failure {
    //持有成功和失败的 block
    [self setCompletionBlockWithSuccess:success failure:failure];
    [self start];
}

调用start方法开始请求,这里调用的start方法是子类YTKRequest中的start重载的方法。

- (void)start {
    //是否忽略缓存
    if (self.ignoreCache) {
        [self startWithoutCache];
        return;
    }

    //是否有未下载完的文件
    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];
        //防止self在外部被释放,效果等同于__strong typeof(self) strongSelf = self;
        YTKRequest *strongSelf = self;
        //成功的代理回调
        [strongSelf.delegate requestFinished:strongSelf];
        //成功的 block 回调
        if (strongSelf.successCompletionBlock) {
            strongSelf.successCompletionBlock(strongSelf);
        }
        //清空当前持有的 block,避免循环引用,self-/->block-->self
        [strongSelf clearCompletionBlock];
    });
}

这段代码主要是判断是否有缓存,有缓存就读取,没缓存就startWithoutCache直接请求。先看有缓存的情况下,怎么取的缓存,请看下面的从缓存读取数据部分。

从缓存读取数据

通过上面代码中的判断条件我们可以知道,[self loadCacheWithError:nil]会试着读取缓存,成功则接着走下面的方法从处理缓存,否则需要做请求。

- (BOOL)loadCacheWithError:(NSError * _Nullable __autoreleasing *)error {
    // Make sure cache time in valid.
    //需要用户手动设置缓存时间,默认是-1
    if ([self cacheTimeInSeconds] < 0) {
        if (error) {
            *error = [NSError errorWithDomain:YTKRequestCacheErrorDomain code:YTKRequestCacheErrorInvalidCacheTime userInfo:@{ NSLocalizedDescriptionKey:@"Invalid cache time"}];
        }
        return NO;
    }

    // Try load metadata.
    //如果有缓存元数据,则读取缓存元数据,没有则return NO;(告诉上层调用者没有缓存,老实请求吧)
    if (![self loadCacheMetadata]) {
        if (error) {
            *error = [NSError errorWithDomain:YTKRequestCacheErrorDomain code:YTKRequestCacheErrorInvalidMetadata userInfo:@{ NSLocalizedDescriptionKey:@"Invalid metadata. Cache may not exist"}];
        }
        return NO;
    }

    // Check if cache is still valid.
    if (![self validateCacheWithError:error]) {
        return NO;
    }

    // Try load cache.读取缓存,此时这个操作就把c缓存读取下来了
    if (![self loadCacheData]) {
        if (error) {
            *error = [NSError errorWithDomain:YTKRequestCacheErrorDomain code:YTKRequestCacheErrorInvalidCacheData userInfo:@{ NSLocalizedDescriptionKey:@"Invalid cache data"}];
        }
        return NO;
    }

    return YES;
}

1、[self cacheTimeInSeconds],判断是否给这个请求命令设置缓存时间了,没设置的话代表这个请求不需要缓存。
2、[self loadCacheMetadata],如果有缓存元数据,则读取缓存元数据,没有则return NO;(告诉上层调用者没有缓存,老实请求吧),这个元数据在下面会解释。
3、[self validateCacheWithError:error],判断是否是有效的缓存数据。
4、[self loadCacheData],读取缓存,此时这个操作就把c缓存读取下来了。
下面从以2、3、4分为三部分来看一下具体怎么读的缓存

Part1 读取缓存元数据

loadCacheMetadata是试图从缓存元数据路径下,将归档序列化形式存储的缓存元数据读取出来。

- (BOOL)loadCacheMetadata {
    NSString *path = [self cacheMetadataFilePath];
    NSFileManager * fileManager = [NSFileManager defaultManager];
    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;
}

cacheMetadataFilePath是缓存元数据的路径,进里面简单看看存在哪?

//创建名为LazyRequestCache的目录存放缓存
- (NSString *)cacheBasePath {
    NSString *pathOfLibrary = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    NSString *path = [pathOfLibrary stringByAppendingPathComponent:@"LazyRequestCache"];

    // Filter cache base path
    NSArray> *filters = [[YTKNetworkConfig sharedConfig] cacheDirPathFilters];
    if (filters.count > 0) {
        for (id f in filters) {
            path = [f filterCacheDirPath:path withRequest:self];
        }
    }

    [self createDirectoryIfNeeded:path];
    return path;
}
//缓存的名字
- (NSString *)cacheFileName {
    NSString *requestUrl = [self requestUrl];
    NSString *baseUrl = [YTKNetworkConfig sharedConfig].baseUrl;
    id argument = [self cacheFileNameFilterForRequestArgument:[self requestArgument]];
    NSString *requestInfo = [NSString stringWithFormat:@"Method:%ld Host:%@ Url:%@ Argument:%@",
                             (long)[self requestMethod], baseUrl, requestUrl, argument];
    NSString *cacheFileName = [YTKNetworkUtils md5StringFromString:requestInfo];
    return cacheFileName;
}
//缓存元数据的路径
- (NSString *)cacheMetadataFilePath {
    NSString *cacheMetadataFileName = [NSString stringWithFormat:@"%@.metadata", [self cacheFileName]];
    NSString *path = [self cacheBasePath];
    path = [path stringByAppendingPathComponent:cacheMetadataFileName];
    return path;
}

我们通过代码可以知道,缓存元数据是以文件序列化形式存储的,什么是缓存元数据?

@interface YTKCacheMetadata : NSObject

@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;//app版本号

@end

目前我们知道的缓存元数据就是这个。

Part2判断是否是有效的缓存元数据

validateCacheWithError是通过cache的过期时间、版本号、敏感数据以及app的版本是否符合来判断缓存数据是否有效的。

- (BOOL)validateCacheWithError:(NSError * _Nullable __autoreleasing *)error {
    // Date
    NSDate *creationDate = self.cacheMetadata.creationDate;
    NSTimeInterval duration = -[creationDate timeIntervalSinceNow];
    //判断cache过期了没有。
    if (duration < 0 || duration > [self cacheTimeInSeconds]) {
        if (error) {
            *error = [NSError errorWithDomain:YTKRequestCacheErrorDomain code:YTKRequestCacheErrorExpired userInfo:@{ NSLocalizedDescriptionKey:@"Cache expired"}];
        }
        return NO;
    }
    // Version,判断cache版本号是否正确
    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;
    }
    // Sensitive data,敏感数据是否相同。
    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 version,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;
}
Part3 读取缓存数据
- (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];
        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;
}
- (NSString *)cacheFilePath {
    NSString *cacheFileName = [self cacheFileName];
    NSString *path = [self cacheBasePath];
    path = [path stringByAppendingPathComponent:cacheFileName];
    return path;
}

从代码中我们可以得知,缓存数据是以二进制的形式存储在LazyRequestCache文件夹下的,缓存元数据也是存储在这个文件夹下。

@interface YTKRequest()

@property (nonatomic, strong) NSData *cacheData;
@property (nonatomic, strong) NSString *cacheString;
@property (nonatomic, strong) id cacheJSON;
@property (nonatomic, strong) NSXMLParser *cacheXML;

@property (nonatomic, strong) YTKCacheMetadata *cacheMetadata;
@property (nonatomic, assign) BOOL dataFromCache;

@end

缓存数据读取成功后,上面的对应属性就会有缓存值,并开始对缓存的处理,dataFromCache变为YES,即在判断是否有缓存时,如果有就把缓存取出来了,如下面代码所示,

- (void)start {
    ···
    dispatch_async(dispatch_get_main_queue(), ^{
        //处理缓存,保存缓存
        [self requestCompletePreprocessor];
        //做回调数据成功后的处理,需要自行实现
        [self requestCompleteFilter];
        //防止self在外部被释放,效果等同于__strong typeof(self) strongSelf = self;
        YTKRequest *strongSelf = self;
        //成功的代理回调
        [strongSelf.delegate requestFinished:strongSelf];
        //成功的 block 回调
        if (strongSelf.successCompletionBlock) {
            strongSelf.successCompletionBlock(strongSelf);
        }
        //清空当前持有的 block,避免循环引用,self-/->block-->self
        [strongSelf clearCompletionBlock];
    });
}
- (void)requestCompletePreprocessor {
    [super requestCompletePreprocessor];
    //数据 + 元数据(请求数据的描述)
    if (self.writeCacheAsynchronously) {
        dispatch_async(ytkrequest_cache_writing_queue(), ^{
            [self saveResponseDataToCacheFile:[super responseData]];
        });
    } else {
        [self saveResponseDataToCacheFile:[super responseData]];
    }
}

- (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);
            }
        }
    }
}
  • requestCompletePreprocessor方法做的是更新缓存的操作,从saveResponseDataToCacheFile方法中可以看出,更新的是缓存数据和缓存元数据两个缓存,当然当前在start请求时,是从缓存中拿到的数据,所以不需要进行更新。
  • requestCompleteFilter做回调数据成功后的处理,需要自行在具体命令类中实现
  • 请求回调部分:可以通过delegate代理回调,也可以通过block回调。
从缓存读取数据的总结
  • YTKNetwork的缓存数据分为缓存元数据和缓存数据NSData,缓存元数据是对缓存数据的描述。
  • 读取缓存进行了四重验证:
    1、是否设置了缓存时间
    2、获取缓存元数据
    3、判断缓存元数据是否有效
    4、获取缓存数据
    只要有一步错误就证明没有缓存,需要重新请求获取数据。

从网络请求数据

如果没能从缓存中获取到数据,则直接进行网络请求

- (void)startWithoutCache {
    [self clearCacheVariables];
    //
    [super start];
}
先清空缓存变量
- (void)clearCacheVariables {
    _cacheData = nil;
    _cacheXML = nil;
    _cacheJSON = nil;
    _cacheString = nil;
    _cacheMetadata = nil;
    _dataFromCache = NO;
}

调用父类YTKBaseRequeststart方法。

#import "YTKBaseRequest.h"
- (void)start {
    //响应代理即将开始请求,requestWillStart
    [self toggleAccessoriesWillStartCallBack];
    [[YTKNetworkAgent sharedAgent] addRequest:self];
}
  • toggleAccessoriesWillStartCallBack:该方法定义在YTKNetworkPrivate中,这个类中包含了一些YTKBaseRequest的分类,这个方法就在RequestAccessory分类中,主要用于通知Request的状态。
  • [[YTKNetworkAgent sharedAgent] addRequest:self];:调用YTKNetworkAgentaddRequest开始真正的请求。

那么这里就分两步,Part1是请求状态的改变通知,Part2是开启Task请求

Part1 请求状态的改变通知

YTKBaseRequest有个代理YTKRequestAccessory,用于通知用户请求的状态的。用户只要遵守YTKRequestAccessory这个代理协议,就可以通过下面的代理方法监听到request状态的改变

@protocol YTKRequestAccessory 
@optional
- (void)requestWillStart:(id)request;
- (void)requestWillStop:(id)request;
- (void)requestDidStop:(id)request;

@end

toggleAccessories系列的方法定义在YTKNetworkPrivate中的分类里

#import "YTKNetworkPrivate.h"
@interface YTKBaseRequest (RequestAccessory)

- (void)toggleAccessoriesWillStartCallBack;
- (void)toggleAccessoriesWillStopCallBack;
- (void)toggleAccessoriesDidStopCallBack;

@end

@implementation YTKBaseRequest (RequestAccessory)

- (void)toggleAccessoriesWillStartCallBack {
    for (id accessory in self.requestAccessories) {
        if ([accessory respondsToSelector:@selector(requestWillStart:)]) {
            [accessory requestWillStart:self];
        }
    }
}

- (void)toggleAccessoriesWillStopCallBack {
    for (id accessory in self.requestAccessories) {
        if ([accessory respondsToSelector:@selector(requestWillStop:)]) {
            [accessory requestWillStop:self];
        }
    }
}

- (void)toggleAccessoriesDidStopCallBack {
    for (id accessory in self.requestAccessories) {
        if ([accessory respondsToSelector:@selector(requestDidStop:)]) {
            [accessory requestDidStop:self];
        }
    }
}
@end
Part2 开启Task请求
- (void)addRequest:(YTKBaseRequest *)request {
    NSParameterAssert(request != nil);

    NSError * __autoreleasing requestSerializationError = nil;
    //1、获取NSURLSessionTask网络请求任务
    NSURLRequest *customUrlRequest= [request buildCustomUrlRequest];
    if (customUrlRequest) {
        //1.1用户是否有自定义请求,如果有自定义请求需要在具体命令类中实现这个方法
        __block NSURLSessionDataTask *dataTask = nil;
        dataTask = [_manager dataTaskWithRequest:customUrlRequest completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
            [self handleRequestResult:dataTask responseObject:responseObject error:error];
        }];
        request.requestTask = dataTask;
    } else {
        //1.2如果没有自定义请求,则设置NSURLSessionTask
        request.requestTask = [self sessionTaskForRequest:request error:&requestSerializationError];
    }
    
    if (requestSerializationError) {
        [self requestDidFailWithRequest:request error:requestSerializationError];
        return;
    }

    NSAssert(request.requestTask != nil, @"requestTask should not be nil");

    // Set request task priority
    // !!Available on iOS 8 +
    //设置请求的优先级
    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;
        }
    }

    // Retain request
    YTKLog(@"Add request: %@", NSStringFromClass([request class]));
    //保存request到字典中,taskIdentifier为key,request为value
    [self addRequestToRecord:request];
    //开启请求任务
    [request.requestTask resume];
}

addRequest方法主要做的事情就是开启Task网络请求任务,它需要经过三步:

  • 1、获取Task:调用buildCustomUrlRequest判断用户是否实现了该方法创建自定义的request请求,如果有则直接通过AFN的dataTaskWithRequest方法获取Task,否则需要进行一系列设置,再通过AFN的dataTaskWithRequest获取Task。
  • 2、保存request:获得了Task,为了方便以后作取消任务等操作,将Task保存在了字典中,taskIdentifier为key,request为value
  • 3、开启Task请求任务:[request.requestTask resume];
Part2.1 设置Task请求

一个网络请求需要如下参数
1、method:获取请求类型:默认是Get请求
2、url:请求地址
3、params:请求参数
4、constructingBlock:多表单情况下的formData
5、requestSerializer:requestSerializer实例
YTKNetwork的操作如下

- (NSURLSessionTask *)sessionTaskForRequest:(YTKBaseRequest *)request error:(NSError * _Nullable __autoreleasing *)error {
    //获取请求类型:默认是Get请求
    YTKRequestMethod method = [request requestMethod];
    //获取请求url
    NSString *url = [self buildRequestUrl:request];
    //获取请求参数
    id param = request.requestArgument;
    //拼接当前的多表单,formData
    AFConstructingBlock constructingBlock = [request constructingBodyBlock];
    //获取requestSerializer实例
    AFHTTPRequestSerializer *requestSerializer = [self requestSerializerForRequest:request];

    switch (method) {
        case YTKRequestMethodGET:
            if (request.resumableDownloadPath) {
                return [self downloadTaskWithDownloadPath:request.resumableDownloadPath requestSerializer:requestSerializer URLString:url parameters:param progress:request.resumableDownloadProgressBlock error:error];
            } else {
                return [self dataTaskWithHTTPMethod:@"GET" requestSerializer:requestSerializer URLString:url parameters:param error:error];
            }
        case YTKRequestMethodPOST:
            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];
    }
}

我们以POST请求为例,

- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                               requestSerializer:(AFHTTPRequestSerializer *)requestSerializer
                                       URLString:(NSString *)URLString
                                      parameters:(id)parameters
                       constructingBodyWithBlock:(nullable void (^)(id  formData))block
                                           error:(NSError * _Nullable __autoreleasing *)error {
    NSMutableURLRequest *request = nil;
    //判断是否设置了AFConstructingBlock多表单请求
    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];
    }

    __block NSURLSessionDataTask *dataTask = nil;
    dataTask = [_manager dataTaskWithRequest:request
                           completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *_error) {
                               [self handleRequestResult:dataTask responseObject:responseObject error:_error];
                           }];

    return dataTask;
}

这段代码就是开始做请求了,利用AFN的manager做的网络请求,返回NSURLSessionDataTask给上层,以便控制任务的状态和保存request请求。
并在当前类YTKNetworkAgent中处理请求的回调handleRequestResult:responseObject:error

- (void)handleRequestResult:(NSURLSessionTask *)task responseObject:(id)responseObject error:(NSError *)error {
   //加锁防止在获取request时不准确
    Lock();
    YTKBaseRequest *request = _requestsRecord[@(task.taskIdentifier)];
    Unlock();
    //判断如果request是空的话,表示请求可能被取消或移除了
    if (!request) {
        return;
    }

    /** 为什么使用__autoreleasing
     http://ihomway.cc/2016/12/28/NSError-autoreleasing-error%E4%B8%AD%E7%9A%84-autoreleasing%E4%BF%AE%E9%A5%B0%E7%AC%A6/
     */
    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];
        [request clearCompletionBlock];
    });
}

这个方法的作用是
1、更新request请求的回调参数responseObject
2、执行请求成功或失败的后续操作
3、移除request,以及成功、失败的block回调,避免循环引用

1、3操作不必多说,就像代码展示的一样,主要是第2步,成功和失败的后续处理

请求成功后,requestDidSucceedWithRequest的代码如下

- (void)requestDidSucceedWithRequest:(YTKBaseRequest *)request {
    //磁盘缓存
    @autoreleasepool {
        /** 请求回调需要做的操作,这里面主要就是缓存 */
        [request requestCompletePreprocessor];
    }
    dispatch_async(dispatch_get_main_queue(), ^{
        [request toggleAccessoriesWillStopCallBack];
        [request requestCompleteFilter];

        if (request.delegate != nil) {
            [request.delegate requestFinished:request];
        }
        if (request.successCompletionBlock) {
            request.successCompletionBlock(request);
        }
        [request toggleAccessoriesDidStopCallBack];
    });
}
  • requestCompletePreprocessor:我们在一开始讨论个这个方法,主要做的是更新缓存的操作。
  • toggleAccessoriesWillStopCallBacktoggleAccessoriesDidStopCallBack:更新request请求状态,遵守YTKRequestAccessory这个代理协议,就可以通过代理方法监听到request状态的改变。
  • requestCompleteFilter:请求成功后的操作,需要用户自己在具体命令类中实现。
  • 代理和block回调。

请求失败后,requestDidFailWithRequest的代码如下

- (void)requestDidFailWithRequest:(YTKBaseRequest *)request error:(NSError *)error {
    request.error = error;
    YTKLog(@"Request %@ failed, status code = %ld, error = %@",
           NSStringFromClass([request class]), (long)request.responseStatusCode, error.localizedDescription);

    // Save incomplete download data.
    //如果是下载任务,则保存已经下载完的数据到resumableDownloadPath路径下,以便下次接着下载
    NSData *incompleteDownloadData = error.userInfo[NSURLSessionDownloadTaskResumeData];
    if (incompleteDownloadData) {
        [incompleteDownloadData writeToURL:[self incompleteDownloadTempPathForDownloadPath:request.resumableDownloadPath] atomically:YES];
    }

    // Load response from file and clean up if download task failed.
    // 如果是下载任务,则从url路径中获取responseData和responseString回调,并把responseObject置空
    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;
    }

    @autoreleasepool {
        [request requestFailedPreprocessor];
    }
    //request请求状态改变的通知和代理、block的回调
    dispatch_async(dispatch_get_main_queue(), ^{
        [request toggleAccessoriesWillStopCallBack];
        [request requestFailedFilter];

        if (request.delegate != nil) {
            [request.delegate requestFailed:request];
        }
        if (request.failureCompletionBlock) {
            request.failureCompletionBlock(request);
        }
        [request toggleAccessoriesDidStopCallBack];
    });
}

请求失败的处理主要是针对下载任务,
1、如果是下载任务,则保存已经下载完的数据到resumableDownloadPath路径下,以便下次接着下载;从url路径中获取responseData和responseString回调,并把responseObject置空。
2、发送请求的通知和回调。

从网络请求数据的总结

1、整理NSURLSessionTask请求,针对不同的request请求做处理,并以TaskIdentifierkey存储request,方便日后cancelRequest等操作。
2、处理回调handleRequestResult:responseObject:error
2.1、更新request请求的回调参数responseObject
2.2、执行请求成功或失败的后续操作
2.3、移除request,以及成功、失败的block回调,避免循环引用

你可能感兴趣的:(iOS YTKNetwork源码解析)