AFNetworking源码解析

很久之前就打算解读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进行网络请求主要分为以下几个步骤:

  • 1.创建configuration对象,这个这个对象可以设置进行网络请求的一些属性,例如timeoutIntervalForRequest,allowsCellularAccess等等。
  • 2.通过configuration对象创建session对象,一个session对象可以管理多个task。
  • 3.通过URL创建request对象,通俗的理解,一个网络请求包括起始行,请求头,请求体,那么一个request对象也就封装了起始行,请求头,请求体这些内容。另外,其实request也可以行使configuration的功能,通过它的属性可以设置timeoutIntervalForRequest,allowsCellularAccess等等,这个后面一点我们会看到。
  • 4.通过sessionrequest来创建task对象。

下面我们使用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的策略,这个策略要说起来比较麻烦,这里我就用三个例子来解释一下具体策略:

  • 1.当parametersNSDictionary类型时
    id parameters = @{@"name" : @"pdd",
                      @"sex" : @"man",
                      };

返回的字符串:

name=pdd&sex=man
  • 2.当parameters是NSDictionary且NSDictionary中含有NSDictionary
    id parameters = @{@"name" : @"pdd",
                      @"sex" : @"man",
                      @"friends" : @{@"name" : @"xiaoming", @"sex" : @"man"}
                      };

返回的字符串:

name=pdd&sex=man&friends[name]=xiaoming&friends[sex]=man
  • 3.当parametersNSArray类型时
    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这个类的作用:

  • 1.分担一部分AFURLSessionManager的工作,在一些AFURLSessionManager实现的代理方法中,直接调用了AFURLSessionManagerTaskDelegate的相关方法。这个在之前讲过,不再赘述。
  • 2.对于上传和下载,监听上传和下载的进度并产生回调,具体过程如下:
    1>
    2>
    3>
    9E530F9C-93C7-4003-88F4-8F89262FD102.png

    到这里,一个完整的GET请求就完成了。

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。在这个类中有几个关键的属性,其作用如下:

  • requestSerializer
    这是进行请求序列化的属性,它的默认类是AFHTTPRequestSerializer。这个属性的主要作用,就是为request设置请求的起始行,请求头,请求体,还有其他例如请求超时时间等设置。如果我们想要把请求的parameters封装为特殊的格式如Json或plist,那么我们可以使用AFHTTPRequestSerializer的子类AFJSONSerializerAFPropertyListRequestSerializer
  • responseSerializer
    这个类是进行响应序列化的属性,默认是AFJSONResponseSerializerAFJSONResponseSerializer主要做了两个工作,第一个是验证response的有效性,验证有效性从两方面去验证,一方面是response的MIME类型是否在已设置的acceptableContentTypes,另一方面是验证response的statusCode是否在200-299范围内,两个方面有一个方面不符合则视为无效的response,不用解析,直接报错;第二个工作是对数据进行解析,从网络端得到的数据都是NSData类型的,需要对它进行转化,转化为我们需要的类型,如json,plist等。
  • session
    在我们创建一个dataTask的时候,不仅需要request,还需要session对象,在AFURLSessionManager中,创建了session对象来创建dataTask。
  • reachabilityManager
    这个属性创建出来是为了监听设备的网络状态使用的。
  • securityPolicy
    在网络请求的过程中遇到认证挑战时使用这个属性。
  • AFURLSsssionManagerTaskDelegate
    每一个dataTask都对应了一个AFURLSsssionManagerTaskDelegate对象,这种一一对应的关系存储在AFURLSessionManager中,在AFURLSsssionManagerTaskDelegate中实现了NSURLSessionDelegate,NSURLSessionTaskDelegate等协议,这其中有些协议的实现交给了AFURLSsssionManagerTaskDelegate去成成。并且AFURLSsssionManagerTaskDelegate还监听了dataTask的上传下载进度。

使用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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容