iOS开发 Objective-CiOS收藏

AFNetworking源码解析

2019-05-28  本文已影响0人  雪山飞狐_91ae

很久之前就打算解读AFN的源码,也尝试看了几遍,但是可能是由于自己功力还不够,总是感觉看起来很吃力,觉得在云里雾里。最近又重新开始尝试阅读AFN的源码,发现好了很多。接下来就把我所理解的记录下来,一是加深印象,二是时间久了可以拿出来翻阅一下。

AFNetworking是一个用来做网络请求的第三方库,它是对NSURLSession做网络请求的一个封装。下面我们先从一个简单的GET请求开始,从整体上把握一下AFNetworking是如何做网络请求的。

整体阅读

在整体阅读源码之前我们先看一下使用NSURLSession做网络请求的步骤:

    //创建Configuration对象,并设置各种属性
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    configuration.timeoutIntervalForRequest = 10.0;
    configuration.allowsCellularAccess = YES;
    
    //通过Configuration创建session,一个session可以管理多个task
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186.32812/login"];
    
    //通过URL创建request
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    //设置request的请求方法和请求体
    request.HTTPMethod = @"POST";
    request.HTTPBody = [@"username=520it&pwd=520it&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
    
    //通过session和request来创建task
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
    }];
    
    [dataTask resume];

总结一下,使用NSURLSession进行网络请求主要分为以下几个步骤:

下面我们使用AFNetworking来做一个网络请求:

    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    NSString *url = @"http://120.25.226.186.32812";
    [manager GET:url parameters:nil progress:^(NSProgress * _Nonnull downloadProgress) {

    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {

    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

    }];

这里我们也可以看到,使用AFNetworking进行一个网络请求非常的简单,点进去:

- (NSURLSessionDataTask *)GET:(NSString *)URLString
                   parameters:(id)parameters
                     progress:(void (^)(NSProgress * _Nonnull))downloadProgress
                      success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
                      failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{

    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                        URLString:URLString
                                                       parameters:parameters
                                                   uploadProgress:nil
                                                 downloadProgress:downloadProgress
                                                          success:success
                                                          failure:failure];

    [dataTask resume];

    return dataTask;
}

这个很简单,创建了一个dataTask然后启动了这个task。接下来看一下dataTask是如何创建的:

- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                       URLString:(NSString *)URLString
                                      parameters:(id)parameters
                                  uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
                                downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                                         success:(void (^)(NSURLSessionDataTask *, id))success
                                         failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
    NSError *serializationError = nil;
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];

    __block NSURLSessionDataTask *dataTask = nil;
    dataTask = [self dataTaskWithRequest:request
                          uploadProgress:uploadProgress
                        downloadProgress:downloadProgress
                       completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
        if (error) {
            if (failure) {
                failure(dataTask, error);
            }
        } else {
            if (success) {
                success(dataTask, responseObject);
            }
        }
    }];

    return dataTask;
}

这里分为了两步,第一步是创建了一个NSMutableRequest对象,第二步是使用这个NSMutableRequest对象创建了NSURLSessionDataTask对象,到这里是不是就有点像用NSURLSession做网络请求呢?
我们先看第一步:

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(id)parameters
                                     error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(method);
    NSParameterAssert(URLString);

    NSURL *url = [NSURL URLWithString:URLString];

    NSParameterAssert(url);

    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
    mutableRequest.HTTPMethod = method;
    
    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
            [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
        }
    }

    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];

    return mutableRequest;
}

我们看一下下面用KVC给mutableRequest的属性设值的部分:

    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
            [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
        }
    }

看一下AFHTTPRequestSerializerObservedKeyPaths()这个方法:

static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
    static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))];
    });

    return _AFHTTPRequestSerializerObservedKeyPaths;
}

这个方法很明显,就是把 NSMutableURLRequest的一些属性转化成字符串数组返回。
然后如果self.mutableObservedChangedKeyPaths包括这个数组中的某个属性,那么就给mutableRequest对象的这个属性通过KVC设值。到这里你一定很好奇这个mutableObservedChangedKeyPaths到底是个什么东西。

我们尝试在文章开头使用AFNetworking进行网络请求那个Demo里加上下面这句:

    manager.requestSerializer.allowsCellularAccess = YES;

然后在- (NSMutableURLRequest *)requestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(id)parameters error:(NSError *__autoreleasing *)error这个方法里打断点,打印self.mutableObservedChangedKeyPaths的值,打印出来的结果应该是

Demo
那么这就很明显了,也就是我们在外面通过requestSerializer设置的一些网络请求的属性,在内部转化到了self.mutableObservedChangedKeyPaths这个数组内部,再通过比对给request的这些属性设值。接下来我们看一下在外部给requestSerializer的一些属性设值是怎么转化到self.mutableObservedChangedKeyPaths这个数组内部的:
- (void)setAllowsCellularAccess:(BOOL)allowsCellularAccess {
    [self willChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
    _allowsCellularAccess = allowsCellularAccess;
    [self didChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
}

当我们设置manager.requestSerializer.allowsCellularAccess = YES;时代码会执行其set方法,在set方法里面,很明显是手动触发了KVO,再接着看,在KVO的回调里:

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(__unused id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    if (context == AFHTTPRequestSerializerObserverContext) {
        if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
            [self.mutableObservedChangedKeyPaths removeObject:keyPath];
        } else {
            [self.mutableObservedChangedKeyPaths addObject:keyPath];
        }
    }
}

这样就把这个key加到了self.mutableObservedChangedKeyPaths数组里。

上面创建mutableRequest的第一步完成了,我在文章开头讲过,一个NSMutableRequest对象封装了请求的起始行,请求头,请求体,除此之外还可以设置请求的一些属性,比如allowsCellularAccess等等。那么第一步也就完成了起始行的配置,以及请求的一些属性的设置,还剩下请求头和请求体的配置,那么第二步就是为了完成这个工作:

    mutableRequest = [[self requestBySerializingRequest:
          mutableRequest withParameters:parameters error:error] mutableCopy];
创建mutableRequest第二步
这里我们先看第一步:为mutableRequest设置请求头
    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
        if (![request valueForHTTPHeaderField:field]) {
            [mutableRequest setValue:value forHTTPHeaderField:field];
        }
    }];

self.HTTPRequestHeaders是在AFHTTPRequestSerializer的初始化方法里面创建的,self.HTTPRequestHeaders主要包含两个请求头。即Accept-LanguageUser-Agent。这里就是为mutableRequest设置了这两个请求头。

继续看第二步:将参数进行转化,并用=,&连起来
这一步的核心就是query = AFQueryStringFromParameters(parameters);这一句,我们看一下AFQueryStringFromParameters()方法的实现:

NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
    NSMutableArray *mutablePairs = [NSMutableArray array];
    for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
        [mutablePairs addObject:[pair URLEncodedStringValue]];
    }

    return [mutablePairs componentsJoinedByString:@"&"];
}

这里的策略就是通过AFQueryStringPairsFromDictionary()这个方法把parameters转化为一个个AFQueryStringPair对象,然后把这些对象都加入mutablePairs这个数组中,最后使用&把数组中的对象连起来,组成一个字符串。

先了解一下AFQueryStringPair这个类,这个类有两个属性,一个是field,一个是value,还有一个方法- (NSString *)URLEncodedStringValue,这个方法就是把fieldvalue这两个属性用=连起来,组成字符串。

- (instancetype)initWithField:(id)field value:(id)value {
    self = [super init];
    if (!self) {
        return nil;
    }

    self.field = field;
    self.value = value;

    return self;
}

- (NSString *)URLEncodedStringValue {
    if (!self.value || [self.value isEqual:[NSNull null]]) {
        return AFPercentEscapedStringFromString([self.field description]);
    } else {
        return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedStringFromString([self.field description]), AFPercentEscapedStringFromString([self.value description])];
    }
}

接下来再来看一下AFQueryStringPairsFromDictionary()这个方法处理parameters的策略,这个策略要说起来比较麻烦,这里我就用三个例子来解释一下具体策略:

    id parameters = @{@"name" : @"pdd",
                      @"sex" : @"man",
                      };

返回的字符串:

name=pdd&sex=man
    id parameters = @{@"name" : @"pdd",
                      @"sex" : @"man",
                      @"friends" : @{@"name" : @"xiaoming", @"sex" : @"man"}
                      };

返回的字符串:

name=pdd&sex=man&friends[name]=xiaoming&friends[sex]=man
    id parameters2 = @[@"pdd", @"man"];

返回的字符串:

[]=pdd&[]=man

第二步结束了,我们再返回去,看第三步,如果方法是GET,HEAD,DELETE,则直接将第二步得到的字符串粘贴在请求URL后面。
第四步,当方法不是上面的方法时,需要为mutableRequest设置Content-Type这个请求头,并且将上面第二步得到的参数字符串作为mutableRequest的请求体。

这里为什么对于GET,HEAD,DELETE这三个方法不需要设置Content-Type这个请求头呢?这是因为根据RFC 7231的规定,只有PUTPOST请求的时候,Content-Type才是必须的。

到这里request的创建和设置就完成了,下面继续看dataTask的创建

- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                               uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                             downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                            completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject,  NSError * _Nullable error))completionHandler {

    __block NSURLSessionDataTask *dataTask = nil;
    url_session_manager_create_task_safely(^{
        dataTask = [self.session dataTaskWithRequest:request];
    });

    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];

    return dataTask;
}

url_session_manager_create_task_safely主要是解决了iOS8之前的一个系统Bug,我简单解释一下这个Bug:

在每一个NSURLSession中,每个NSURLSessionDataTask的taskIdentifier应该是独一无二的,不能和其他的NSURLSessionDataTask重叠。但是在iOS8之前,当我们并行的创建一些NSURLSessionDataTask时,这些NSURLSessionDataTask的taskIdentifier有可能重叠。
解决办法:在iOS8之前使用同步串行队列来实现他们的同步有序创建,在iOS8之后,这个Bug已经被修复。

我们看到,url_session_manager_create_task_safely正是创建了一个同步串行队列:

static void url_session_manager_create_task_safely(dispatch_block_t block) {
    if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) {
        // Fix of bug
        // Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8)
        // Issue about:https://github.com/AFNetworking/AFNetworking/issues/2093
        //url_session_manager_creation_queue()是一个串行队列
        dispatch_sync(url_session_manager_creation_queue(), block);
    } else {
        block();
    }
}

接下来看一下:

    [self addDelegateForDataTask:dataTask 
                           uploadProgress:uploadProgressBlock 
                      downloadProgress:downloadProgressBlock 
                     completionHandler:completionHandler];

这个方法非常重要

- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
                uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
              downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
             completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    //NSURLSessionDataTask的代理方法在AFURLSessionManager.m中实现,然后在AFURLSessionManager.m中调用AFURLSessionManagerTaskDelegate的方法
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:dataTask];

    delegate.uploadProgressBlock = uploadProgressBlock;
    delegate.downloadProgressBlock = downloadProgressBlock;
}

我们先来梳理一下AFURLSessionManagerAFURLSessionManagerTaskDelegateNSURLSessionDataTask这三个类之间的关系:

创建session.png

上图中,在AFURLSessionManager这个类中,创建了session对象,并指定自己为session的delegate,这意味着AFURLSessionManager这个类需要实现NSURLSessionDelegateNSURLSessionTaskDelegateNSURLSessionDataDelegateNSURLSessionDownloadDelegate这些协议的方法。

实现协议方法 这里也确实实现了这些代理方法,但是有一些代理方法其实是在代理方法内部交给了AFURLSessionManagerTaskDelegate这个类去实现,比如下面的代理方法: 交给AFURLSessionManagerTaskDelegate.png 所以总结一下就是,AFURLSessionManager需要将NSURLSessionDataTaaskAFURLSessionManagerTaskDelegate对应起来,这样在NSURLSessionDataTaask的代理方法中就可以获取到task对应的AFURLSessionManagerTaskDelegate然后交给它去处理。

再回到代码中

- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    NSParameterAssert(task);
    NSParameterAssert(delegate);

    [self.lock lock];
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    //KVO,为task添加观察者,观察者为AFURLSessionManagerTaskDelegate
    [delegate setupProgressForTask:task];
    //添加通知
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
                uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
              downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
             completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    //NSURLSessionDataTask的代理方法在AFURLSessionManager.m中实现,然后在AFURLSessionManager.m中调用AFURLSessionManagerTaskDelegate的方法
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:dataTask];

    delegate.uploadProgressBlock = uploadProgressBlock;
    delegate.downloadProgressBlock = downloadProgressBlock;
}

[delegate setupProgressForTask:task];即为task的一些下载和上传的属性使用KVO添加观察者:

添加观察者.png 然后在检测到属性改变的回调里,改变自己的属性值:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    if ([object isKindOfClass:[NSURLSessionTask class]] || [object isKindOfClass:[NSURLSessionDownloadTask class]]) {
        if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
            self.downloadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {
            self.downloadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
            self.uploadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) {
            self.uploadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
        }
    }
    else if ([object isEqual:self.downloadProgress]) {
        if (self.downloadProgressBlock) {
            self.downloadProgressBlock(object);
        }
    }
    else if ([object isEqual:self.uploadProgress]) {
        if (self.uploadProgressBlock) {
            self.uploadProgressBlock(object);
        }
    }
}

总结一下AFURLSessionManagerTaskDelegate这个类的作用:

AFHTTPRequestSerializer

AFHTTPRequestSerializer这个类的作用是将request序列化,通俗的讲,就是为request设置起始行,请求头,请求体,以及一些其他的网络请求属性。

它有两个子类:

AFHTTPRequestSerializer
由于AFHTTPRequestSerializer遵循AFURLRequestSerialization协议,所以其两个子类也遵循这个协议,这个协议只有一个方法:
- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(nullable id)parameters
                                        error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;

默认情况下是使用AFHTTPRequestSerializer,但是当我们为manager.requestSerializer赋值时,可使用AFJSONRequestSerializerAFPropertyListRequestSerializer:

    manager.requestSerializer = [AFJSONRequestSerializer serializer];

AFHTTPRequestSerializer,AFJSONRequestSerializer,AFPropertyListRequestSerializer三个类的不同体现在对协议方法的实现上:
AFHTTPRequestSerializer:

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(id)parameters
                                        error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(request);

    NSMutableURLRequest *mutableRequest = [request mutableCopy];

    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
        if (![request valueForHTTPHeaderField:field]) {
            [mutableRequest setValue:value forHTTPHeaderField:field];
        }
    }];

    NSString *query = nil;
    if (parameters) {
        //queryStringSerialization是一个Block
        if (self.queryStringSerialization) {
            NSError *serializationError;
            query = self.queryStringSerialization(request, parameters, &serializationError);

            if (serializationError) {
                if (error) {
                    *error = serializationError;
                }

                return nil;
            }
        } else {
            //这是一个枚举值,只有这一种情况
            switch (self.queryStringSerializationStyle) {
                case AFHTTPRequestQueryStringDefaultStyle:
                    //把键值对用等号连起来
                    query = AFQueryStringFromParameters(parameters);
                    break;
            }
        }
    }

    //GET, HEAD, DELETE
    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
        if (query && query.length > 0) {
            mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
        }
        
    //如果不是以上三种方法,需要设置Content-Type首部,并把query查询语句加入body中
    } else {
        // #2864: an empty string is a valid x-www-form-urlencoded payload
        if (!query) {
            query = @"";
        }
        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
            [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
        }
        [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
    }

    return mutableRequest;
}

AFJSONRequestSerializer:

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(id)parameters
                                        error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(request);

    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
        return [super requestBySerializingRequest:request withParameters:parameters error:error];
    }

    NSMutableURLRequest *mutableRequest = [request mutableCopy];

    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
        if (![request valueForHTTPHeaderField:field]) {
            [mutableRequest setValue:value forHTTPHeaderField:field];
        }
    }];

    if (parameters) {
        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
            [mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
        }

        [mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]];
    }

    return mutableRequest;
}

AFPropertyListRequestSerializer:

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(id)parameters
                                        error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(request);

    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
        return [super requestBySerializingRequest:request withParameters:parameters error:error];
    }

    NSMutableURLRequest *mutableRequest = [request mutableCopy];

    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
        if (![request valueForHTTPHeaderField:field]) {
            [mutableRequest setValue:value forHTTPHeaderField:field];
        }
    }];

    if (parameters) {
        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
            [mutableRequest setValue:@"application/x-plist" forHTTPHeaderField:@"Content-Type"];
        }

        [mutableRequest setHTTPBody:[NSPropertyListSerialization dataWithPropertyList:parameters format:self.format options:self.writeOptions error:error]];
    }

    return mutableRequest;
}

可以看到,当请求的方法是GET,HEAD, DELETE时,是直接调用了父类的方法的。当请求的方法不是这三个时,才会自行处理,自行处理的第一部分是为mutableRequest设置公共的请求头,第二部分是处理参数,我们以AFURLRequestSerialization为例:

    if (parameters) {
        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
            [mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
        }

        [mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]];
    }

首先是设置Content-Type,然后使用NSJSONSerialization对参数序列化,然后设置为request的请求体。

Type Content-Type data format
HTTP application/x-www-form-urlencoded UTF-8 data
JSON application/json json data
PropertyList application/x-plist p-list

AFHTTPResponseSerializer

AFHTTPResponseSerializer是响应序列化的工具类,它和AFHTTPRequestSerializer很相似,有6个子类,并且遵循AFURLResponseSerialization这个协议。

image
AFURLResponseSerialization协议也只有一个协议方法:
- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
                           data:(nullable NSData *)data
                          error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;

在之前的源码中,我们好像从来没有用到AFHTTPResponseSerializer及其子类,那么它是使用在哪里呢?AFHTTPResponseSerializer及其子类是用在解析从网络端获取的response。我们在AFURLSessionManagerNSURLSessionTaskDelegate这个代理中查看- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error这个方法:

- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];

    // delegate may be nil when completing a task in the background
    if (delegate) {
        [delegate URLSession:session task:task didCompleteWithError:error];

        [self removeDelegateForTask:task];
    }

    if (self.taskDidComplete) {
        self.taskDidComplete(session, task, error);
    }
}

接着查看AFURLSessionManagerTaskDelegate这个类的同样的方法:
这个方法比较复杂,我们找到关键的一句:

            responseObject = [manager.responseSerializer 
                                responseObjectForResponse:task.response 
                                                       data:data error:&serializationError];

然后我们查看这个方法的实现:
我们以AFJSONResponseSerializer为例:


这里我们看一下判断response的有效性的步骤:
- (BOOL)validateResponse:(NSHTTPURLResponse *)response data:(NSData *)data error:(NSError * __autoreleasing *)error这个方法的实现在AFJSONResponseSerializer的父类即AFHTTPResponseSerializer中:
每一个AFHTTPResponseSerializer的子类在初始化时都设置了acceptableContentTypes这个属性,比如AFJSONResponseSerializer是这样初始化的:
    self.acceptableContentTypes =
           [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];

acceptableStatusCodes是在AFHTTPResponseSerializer类中初始化的:

    self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];

即范围为200-299。
上面讲到AFHTTPResponseSerializer有6个子类,他们之间的区别其实就是根据需要把网络请求返回的数据解析成不同类型的数据,区别如下:

Class Accept Serializer
AFJSONResponseSerializer application/json,text/json,text/javascript NSJSONSerialization
AFXMLParserResponseSerializer application/xml,text/xml NSXMLParser
AFXMLDocumentResponseSerializer application/xml,text/xml NSXMLDocument
AFPropertyListResponseSerializer application/x-plist NSPropertyListSerialization
AFImageResponseSerializer image/tiff,image/jpeg,image/gif,image/png,image/ico,image/x-icon,image/bmp,image/x-bmp,image/x-xbitmap,image/x-win-bitmap NSBitmapImageRep
AFCompoundResponseSerializer 多种类型的集合 -

AFNetworkReachabilityManager

AFNetworkReachabilityManager这个类和前面的网络请求类没有直接的关系,我们可以看到,在前面的解读中,也从来没有提到过AFNetworkReachabilityManager这个类。这个类是用来监测设备的网络状态变化的,其使用如下:

    [[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        switch (status) {
            case AFNetworkReachabilityStatusUnknown:
                NSLog(@"未知网络");
                break;
            case AFNetworkReachabilityStatusNotReachable:
                NSLog(@"无网络连接");
                break;
            case AFNetworkReachabilityStatusReachableViaWWAN:
                NSLog(@"蜂窝网络");
                break;
            case AFNetworkReachabilityStatusReachableViaWiFi:
                NSLog(@"WiFi网络");
                break;
            default:
                break;
        }
    }];
    [[AFNetworkReachabilityManager sharedManager] startMonitoring];

当检测到设备的网络状况改变时会调用block。

- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block {
    self.networkReachabilityStatusBlock = block;
}

上面的方法很简单,就是给自己的block属性赋值。
我们再看startMonitoring这个方法:

AFHTTPSessionManager和AFURLSessionManager

AFHTTPSessionManagerAFURLSessionManager的子类,我们在使用的时候大多数时候其实是使用的AFHTTPSessionManager,它更像是AFURLSessionManager对外的一个接口,这个类提供的API很简单:

- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
                   parameters:(nullable id)parameters
                      success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                      failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;

- (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString
                    parameters:(nullable id)parameters
                       success:(nullable void (^)(NSURLSessionDataTask *task))success
                       failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                    parameters:(nullable id)parameters
                       success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                       failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;

- (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString
                   parameters:(nullable id)parameters
                      success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                      failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

- (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString
                     parameters:(nullable id)parameters
                        success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                        failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

- (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString
                      parameters:(nullable id)parameters
                         success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                         failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

以上这些方法都会统一调用AFHTTPSessionManager.m中的一个内部方法:

- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                       URLString:(NSString *)URLString
                                      parameters:(id)parameters
                                  uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
                                downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                                         success:(void (^)(NSURLSessionDataTask *, id))success
                                         failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
    NSError *serializationError = nil;
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    if (serializationError) {
        if (failure) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
#pragma clang diagnostic pop
        }

        return nil;
    }

    __block NSURLSessionDataTask *dataTask = nil;
    dataTask = [self dataTaskWithRequest:request
                          uploadProgress:uploadProgress
                        downloadProgress:downloadProgress
                       completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
        if (error) {
            if (failure) {
                failure(dataTask, error);
            }
        } else {
            if (success) {
                success(dataTask, responseObject);
            }
        }
    }];

    return dataTask;
}

在上面的内部方法中,requestSerializer是父类的属性,后面创建dataTask的方法也是调用的父类的方法。

观察上面的API也可以发现,AFHTTPSessionManager中的API都是比较简单的请求,当我们涉及到文件的上传和下载等,还是要直接使用AFURLSessionManager中的API:

- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
                                         fromFile:(NSURL *)fileURL
                                         progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
                                completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError  * _Nullable error))completionHandler;

- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
                                             progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
                                          destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                    completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;

总结

总结
AFHTTPSessionManager是一个管理类,在这个类的头文件中提供了进行网络请求的API。在这个类中有几个关键的属性,其作用如下:

使用AFNetworking进行网络请求的大概过程为:
1.首先创建一个mutableRequest,设置其请求方法,然后根据用户的设置,设置mutableRequestallowsCellularAccess,timeoutInterval等属性;然后设置mutableRequest的公共请求头,主要是两个,User-AgentAccept-Language;再把请求的parameters转化为键值对,用=,&连起来组成字符串,如果请求方法是GET,HEAD, DELETE这三个方法之一,则直接把parameters字符串粘贴在请求URL后面,否则就要把parameters字符串设置成请求的请求体,并设置Content-Type请求头;到这里request相关都配置好了,并且这些配置都是在requestSerializer相关类中完成的。
2.创建dataTask,首先创建一个session对象,根据session对象和之前创建的request对象来创建一个dataTask,然后创建一个AFURLSessionManagerTaskDelegate的对象,把这个对象和dataTask关联起来,让AFURLSessionManagerTaskDelegate对象通过KVO监听dataTask的上传下载进度,并执行相应的回调。
3.通过resume启动dataTask。
4.在请求完成的代理方法中,会使用responseSerializer来处理response,首先检查response的有效性,然后将其转化为相应的类型。

参考:https://imliaoyuan.com/2018/02/20/AFNetworking.html

上一篇 下一篇

猜你喜欢

热点阅读