美文网首页
YTKNetWork源码阅读

YTKNetWork源码阅读

作者: HoooChan | 来源:发表于2019-07-08 19:00 被阅读0次

    YTKNetWork是猿题库的团队出的一个基于AFNetWork的网络请求框架,它对请求结果的缓存和接口版本管理做了进一步封装。

    YTKNetWork提供了请求的基类YTKBaseRequest,这个类基本没做什么工作,主要是定义公共的属性和方法。接着YTKRequest继承了YTKBaseRequest,实现了缓存和接口版本管理的主要逻辑。

    基本使用

    我们要通过YTKNetWork发起一个请求,首先要继承YTKRequest实现一个子类,在里面复写请求方法、地址、参数等方法:

    @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
    

    接着就是发起请求:

    RegisterApi *registerApi = [[RegisterApi alloc] initWithUsername:@"username" password:@"password"];
    [registerApi startWithCompletionBlockWithSuccess:^(__kindof YTKBaseRequest * _Nonnull request) {
        if (request.responseObject) {
        }
    } failure:^(__kindof YTKBaseRequest * _Nonnull request) {
        if (request.error) {
        }
    }];
    

    除了startWithCompletionBlockWithSuccess方法,我们也可以直接调用start方法来发起请求,这种方法需要我们设置registerApidelegate来处理请求成功和失败的结果:

    @protocol YTKRequestDelegate <NSObject>
    
    @optional
    ///  Tell the delegate that the request has finished successfully.
    ///
    ///  @param request The corresponding request.
    - (void)requestFinished:(__kindof YTKBaseRequest *)request;
    
    ///  Tell the delegate that the request has failed.
    ///
    ///  @param request The corresponding request.
    - (void)requestFailed:(__kindof YTKBaseRequest *)request;
    
    @end
    

    请求流程

    先来看看startWithCompletionBlockWithSuccess里面做了什么:

    - (void)startWithCompletionBlockWithSuccess:(YTKRequestCompletionBlock)success
                                        failure:(YTKRequestCompletionBlock)failure {
        [self setCompletionBlockWithSuccess:success failure:failure];
        [self start];
    }
    

    这个方法是在YTKBaseRequest里实现的,先是把处理结果的block保存起来,最后也是调用到了start这个函数。

    YTKRequest复写了start函数:

    - (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];
        });
    }
    

    主要工作:

    • 1、先判断是否忽略缓存,若是则直接发起网络请求;
    • 2、因为下载的请求是不会缓存的,所以也直接发起网络请求;
    • 3、加载缓存的数据,如果失败则发起网络请求;
    • 4、标记结果来自缓存,处理请求成功的结果。

    从缓存获取数据

    来看看从缓存加载数据的代码loadCacheWithError

    - (BOOL)loadCacheWithError:(NSError * _Nullable __autoreleasing *)error {
        // Make sure cache time in valid.
        if ([self cacheTimeInSeconds] < 0) {
            if (error) {
                *error = [NSError errorWithDomain:YTKRequestCacheErrorDomain code:YTKRequestCacheErrorInvalidCacheTime userInfo:@{ NSLocalizedDescriptionKey:@"Invalid cache time"}];
            }
            return NO;
        }
    
        // Try load metadata.
        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.
        if (![self loadCacheData]) {
            if (error) {
                *error = [NSError errorWithDomain:YTKRequestCacheErrorDomain code:YTKRequestCacheErrorInvalidCacheData userInfo:@{ NSLocalizedDescriptionKey:@"Invalid cache data"}];
            }
            return NO;
        }
    
        return YES;
    }
    

    主要任务:

    • 1、[self cacheTimeInSeconds]返回的是缓存数据过期的时间,如果小于0,那所有的缓存数据都会过期,所以从缓存获取失败;
    • 2、加载metadata,它包含了缓存数据的版本和创建时间等等;
    • 3、判断缓存数据是否有效;
    • 4、来到真正加载缓存数据的方法。

    先看看加载metadata的方法:

    - (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;
    }
    

    metadata的缓存地址

    - (NSString *)cacheMetadataFilePath {
        NSString *cacheMetadataFileName = [NSString stringWithFormat:@"%@.metadata", [self cacheFileName]];
        NSString *path = [self cacheBasePath];
        path = [path stringByAppendingPathComponent:cacheMetadataFileName];
        return path;
    }
    

    获取basepath的方法:

    - (NSString *)cacheBasePath {
        NSString *pathOfLibrary = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
        NSString *path = [pathOfLibrary stringByAppendingPathComponent:@"LazyRequestCache"];
        // 默认的缓存地址
        // ***/Application/823D3A1D-33A6-4625-A03C-5B7FE5F29EB1/Library/LazyRequestCache
    
        // Filter cache base path
        NSArray<id<YTKCacheDirPathFilterProtocol>> *filters = [[YTKNetworkConfig sharedConfig] cacheDirPathFilters];
        if (filters.count > 0) {
            for (id<YTKCacheDirPathFilterProtocol> f in filters) {
                path = [f filterCacheDirPath:path withRequest:self];
            }
        }
    
        [self createDirectoryIfNeeded:path];
        return path;
    }
    

    默认的缓存地址是:***/Application/823D3A1D-33A6-4625-A03C-5B7FE5F29EB1/Library/LazyRequestCache,如果在YTKNetworkConfig里面配置了自定义的缓存地址就会选择自定义的缓存地址。

    接下来就是创建缓存文件夹:

    - (void)createDirectoryIfNeeded:(NSString *)path {
        NSFileManager *fileManager = [NSFileManager defaultManager];
        BOOL isDir;
        if (![fileManager fileExistsAtPath:path isDirectory:&isDir]) {
            [self createBaseDirectoryAtPath:path];
        } else {
            if (!isDir) {
                NSError *error = nil;
                [fileManager removeItemAtPath:path error:&error];
                [self createBaseDirectoryAtPath:path];
            }
        }
    }
    

    先判断文件路径是否已经存在,如果不存在或者不是文件夹则都需要去创建:

    - (void)createBaseDirectoryAtPath:(NSString *)path {
        NSError *error = nil;
        [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES
                                                   attributes:nil error:&error];
        if (error) {
            YTKLog(@"create cache directory failed, error = %@", error);
        } else {
            [YTKNetworkUtils addDoNotBackupAttribute:path];
        }
    }
    

    创建成功后一个关键代码:[YTKNetworkUtils addDoNotBackupAttribute:path],主要是告诉iCloud不要自动备份这个文件夹。

    至此我们已经拿到metadata的缓存路径了,如果该文件路径存在,则加载metadate: _cacheMetadata = [NSKeyedUnarchiver unarchiveObjectWithFile:path];

    来看看metadata的模型:

    @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
    

    拿到metadata后就是判断缓存数据是否还可用:

    - (BOOL)validateCacheWithError:(NSError * _Nullable __autoreleasing *)error {
        // Date
        NSDate *creationDate = self.cacheMetadata.creationDate;
        NSTimeInterval duration = -[creationDate timeIntervalSinceNow];
        if (duration < 0 || duration > [self cacheTimeInSeconds]) {
            if (error) {
                *error = [NSError errorWithDomain:YTKRequestCacheErrorDomain code:YTKRequestCacheErrorExpired userInfo:@{ NSLocalizedDescriptionKey:@"Cache expired"}];
            }
            return NO;
        }
        // Version
        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
        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;
    }
    

    这里的主要任务是:

    • 1、判断缓存时间是否已经过期;
    • 2、判断版本是否一致;
    • 3、判断敏感数据是否一致,应该是用来自定义的;
    • 4、判断APP的版本是否一致,如果不一致也会判定为无效缓存;

    判断缓存数据有效后就会开始真正加载缓存的数据:

    - (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;
    }
    

    看看缓存文件名是如何生成:

    - (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;
    }
    

    用请求方法、地址、参数拼接字符串再通过md5加密而成。

    拿到缓存数据后就开始按照解析类型对数据进行解析。

    以上是通过缓存取数据的流程,从缓存取失败后就开始调用远程接口。

    从远程获取数据

    startWithoutCache

    - (void)startWithoutCache {
        [self clearCacheVariables];
        [super start];
    }
    

    先清除缓存数据,再开始请求。

    - (void)start {
        [self toggleAccessoriesWillStartCallBack];
        [[YTKNetworkAgent sharedAgent] addRequest:self];
    }
    

    YTKNetworkAgent的初始化

    - (instancetype)init {
        self = [super init];
        if (self) {
            _config = [YTKNetworkConfig sharedConfig];
            _manager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:_config.sessionConfiguration];
            _requestsRecord = [NSMutableDictionary dictionary];
            _processingQueue = dispatch_queue_create("com.yuantiku.networkagent.processing", DISPATCH_QUEUE_CONCURRENT);
            _allStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(100, 500)];
            pthread_mutex_init(&_lock, NULL);
    
            _manager.securityPolicy = _config.securityPolicy;
            _manager.responseSerializer = [AFHTTPResponseSerializer serializer];
            // Take over the status code validation
            _manager.responseSerializer.acceptableStatusCodes = _allStatusCodes;
            _manager.completionQueue = _processingQueue;
        }
        return self;
    }
    

    初始化的过程做了以下事情:

    • 1、初始化YTKNetworkConfig,YTKNetworkConfig主要保存了baseUrl、cdnUrl等等,还可以这这里配置NSURLSessionConfiguration,用来初始化AFHTTPSessionManager;
    • 2、初始化AFHTTPSessionManager;
    • 3、初始化保存请求记录的容器;
    • 4、创建一个并行队列,用于配置AFHTTPSessionManager的completionQueue,因为AFHTTPSessionManager默认在主队列执行completionBlock;
    • 5、初始化合法的HttpCode,AFHTTPSessionManager会根据这个来判断结果是否有效从而决定请求是成功还是失败,这里允许所有的返回码;
    • 6、创建互斥锁;
    • 7、配置AFHTTPSessionManager的相关属性。

    添加请求的代码addRequest

    - (void)addRequest:(YTKBaseRequest *)request {
        NSParameterAssert(request != nil);
    
        NSError * __autoreleasing requestSerializationError = nil;
    
        NSURLRequest *customUrlRequest= [request buildCustomUrlRequest];
        if (customUrlRequest) {
            __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 {
            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]));
        [self addRequestToRecord:request];
        [request.requestTask resume];
    }
    
    • 1、这一步的主要工作包括要序列化NSURLRequest,而YTKBaseRequest有提供创建自定义NSURLRequest的方法,如果创建了自定义的NSURLRequest就直接调用AFHTTPSessionManager创建NSURLSessionDataTask发起请求。这里用AFHTTPSessionManager可以省去自己处理请求进度和回调处理等工作,只需处理最后的返回结果即可;
    • 2、如果没有创建自定义的NSURLRequest,就会走YTKNetwork的方法:[self sessionTaskForRequest:request error:&requestSerializationError]
    - (NSURLSessionTask *)sessionTaskForRequest:(YTKBaseRequest *)request error:(NSError * _Nullable __autoreleasing *)error {
        YTKRequestMethod method = [request requestMethod];
        NSString *url = [self buildRequestUrl:request];
        id param = request.requestArgument;
        AFConstructingBlock constructingBlock = [request constructingBodyBlock];
        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];
        }
    }
    

    首先需要关注一下创建url的代码NSString *url = [self buildRequestUrl:request];

    - (NSString *)buildRequestUrl:(YTKBaseRequest *)request {
        NSParameterAssert(request != nil);
    
        NSString *detailUrl = [request requestUrl];
        NSURL *temp = [NSURL URLWithString:detailUrl];
        // If detailUrl is valid URL
        if (temp && temp.host && temp.scheme) {
            return detailUrl;
        }
        // Filter URL if needed
        NSArray *filters = [_config urlFilters];
        for (id<YTKUrlFilterProtocol> f in filters) {
            detailUrl = [f filterUrl:detailUrl withRequest:request];
        }
    
        NSString *baseUrl;
        if ([request useCDN]) {
            if ([request cdnUrl].length > 0) {
                baseUrl = [request cdnUrl];
            } else {
                baseUrl = [_config cdnUrl];
            }
        } else {
            if ([request baseUrl].length > 0) {
                baseUrl = [request baseUrl];
            } else {
                baseUrl = [_config baseUrl];
            }
        }
        // URL slash compability
        NSURL *url = [NSURL URLWithString:baseUrl];
    
        if (baseUrl.length > 0 && ![baseUrl hasSuffix:@"/"]) {
            url = [url URLByAppendingPathComponent:@""];
        }
    
        return [NSURL URLWithString:detailUrl relativeToURL:url].absoluteString;
    }
    
    • 1、如果直接在YTKBaseRequest里面配置了完整的url,则直接返回;
    • 2、对url进行自定义的处理;
    • 3、获取baseUrl,以单个请求优先,取不到再去YTKNetworkConfig中取;
    • 4、拼接完整的地址。

    至此拿到了完整的请求url,再回到创建NSURLSessionTask的代码,接下来获取用于序列化请求的requestSerializerAFHTTPRequestSerializer *requestSerializer = [self requestSerializerForRequest:request]

    - (AFHTTPRequestSerializer *)requestSerializerForRequest:(YTKBaseRequest *)request {
        AFHTTPRequestSerializer *requestSerializer = nil;
        if (request.requestSerializerType == YTKRequestSerializerTypeHTTP) {
            requestSerializer = [AFHTTPRequestSerializer serializer];
        } else if (request.requestSerializerType == YTKRequestSerializerTypeJSON) {
            requestSerializer = [AFJSONRequestSerializer serializer];
        }
    
        requestSerializer.timeoutInterval = [request requestTimeoutInterval];
        requestSerializer.allowsCellularAccess = [request allowsCellularAccess];
    
        // If api needs server username and password
        NSArray<NSString *> *authorizationHeaderFieldArray = [request requestAuthorizationHeaderFieldArray];
        if (authorizationHeaderFieldArray != nil) {
            [requestSerializer setAuthorizationHeaderFieldWithUsername:authorizationHeaderFieldArray.firstObject
                                                              password:authorizationHeaderFieldArray.lastObject];
        }
    
        // If api needs to add custom value to 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;
    }
    
    • 1、根据YTKBaseRequest指定的序列化方法创建requestSerializer;
    • 2、设置用户认证信息;
    • 3、设置自定义的头部信息。

    拿到requestSerializer后开始调用相应的方法创建NSURLSessionTask:

        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];
        }
    

    如果resumableDownloadPath不为空则说明是下载,会调用创建下载任务的方法,否则就是普通的请求,看看dataTaskWithHTTPMethod方法:

    - (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                   requestSerializer:(AFHTTPRequestSerializer *)requestSerializer
                                           URLString:(NSString *)URLString
                                          parameters:(id)parameters
                                               error:(NSError * _Nullable __autoreleasing *)error {
        return [self dataTaskWithHTTPMethod:method requestSerializer:requestSerializer URLString:URLString parameters:parameters constructingBodyWithBlock:nil error:error];
    }
    
    - (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;
    
        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;
    }
    

    调用AFHTTPSessionManagerdataTaskWithRequest创建请求,在回调中通过handleRequestResult处理结果:

    - (void)handleRequestResult:(NSURLSessionTask *)task responseObject:(id)responseObject error:(NSError *)error {
        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.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、获取当前的请求对象,如果请求为空,也就是被取消了,则不做任何处理,直接返回;
    • 2、判断responseObject是否是NSData,理论上应该是NSData,因为在初始化AFHTTPSessionManager时设置了responseSerializer就是[AFHTTPResponseSerializer serializer],而AFHTTPSessionManager是直接返回NSData:
    - (id)responseObjectForResponse:(NSURLResponse *)response
                               data:(NSData *)data
                              error:(NSError *__autoreleasing *)error
    {
        [self validateResponse:(NSHTTPURLResponse *)response data:data error:error];
    
        return data;
    }
    
    • 3、根据responseSerializerType解析数据;
    • 4、判断返回结果是否合法,首先根据YTKBaseRequest定义的合法的statusCode来判断,然后再根据jsonValidator来判断返回的json数据格式是否正确。
    • 5、根据请求成功与否来进行处理。

    请求成功后的处理:

    - (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];
        });
    }
    
    • 1、调用requestCompletePreprocessor方法进去处理,这里YTKBaseRequest没有做任何事情,主要是在YTKRequest里面执行缓存的处理:
    - (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);
                }
            }
        }
    }
    

    至此从远程获取数据并保存到本地的流程就走完了。

    相关文章

      网友评论

          本文标题:YTKNetWork源码阅读

          本文链接:https://www.haomeiwen.com/subject/nxnahctx.html