原文出处:AFNetworking到底做了什么?

AFNetworking到底做了什么?

写在开头:
先从最新的AF3.x讲起吧:

AF代码结构图.png

除去Support Files,可以看到AF分为如下5个功能模块:

其核心当然是网络通信模块AFURLSessionManager。大家都知道,AF3.x是基于NSURLSession来封装的。所以这个类围绕着NSURLSession做了一系列的封装。而其余的四个模块,均是为了配合网络通信或对已有UIKit的一个扩展工具包。

这五个模块所对应的类的结构关系图如下所示:

AF架构图.png

其中AFHTTPSessionManager是继承于AFURLSessionManager的,我们一般做网络请求都是用这个类,但是它本身是没有做实事的,只是做了一些简单的封装,把请求逻辑分发给父类AFURLSessionManager或者其它类去做。

首先我们简单的写个get请求:
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc]init];
[manager GET:@"http://localhost" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];

首先我们我们调用了初始化方法生成了一个manager,我们点进去看看初始化做了什么:

- (instancetype)init {
    return [self initWithBaseURL:nil];
}
- (instancetype)initWithBaseURL:(NSURL *)url {
    return [self initWithBaseURL:url sessionConfiguration:nil];
}
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    return [self initWithBaseURL:nil sessionConfiguration:configuration];
}
- (instancetype)initWithBaseURL:(NSURL *)url
           sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
    self = [super initWithSessionConfiguration:configuration];
    if (!self) {
        return nil;
    }
    //对传过来的BaseUrl进行处理,如果有值且最后不包含/,url加上"/"
  //--经一位热心读者更正...以后注释也一定要走心啊...不能误导大家...
    if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
        url = [url URLByAppendingPathComponent:@""];
    }
    self.baseURL = url;
    self.requestSerializer = [AFHTTPRequestSerializer serializer];
    self.responseSerializer = [AFJSONResponseSerializer serializer];
    return self;
}

直接来到父类AFURLSessionManager的初始化方法:

- (instancetype)init {
    return [self initWithSessionConfiguration:nil];
}
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super init];
    if (!self) {
        return nil;
    }
    if (!configuration) {
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    }
    self.sessionConfiguration = configuration;
    self.operationQueue = [[NSOperationQueue alloc] init];
    //queue并发线程数设置为1
    self.operationQueue.maxConcurrentOperationCount = 1;
    //注意代理,代理的继承,实际上NSURLSession去判断了,你实现了哪个方法会去调用,包括子代理的方法!
    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
    //各种响应转码
    self.responseSerializer = [AFJSONResponseSerializer serializer];
    //设置默认安全策略
    self.securityPolicy = [AFSecurityPolicy defaultPolicy];
#if !TARGET_OS_WATCH
    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
    // 设置存储NSURL task与AFURLSessionManagerTaskDelegate的词典(重点,在AFNet中,每一个task都会被匹配一个AFURLSessionManagerTaskDelegate 来做task的delegate事件处理) ===============
    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
    //  设置AFURLSessionManagerTaskDelegate 词典的锁,确保词典在多线程访问时的线程安全
    self.lock = [[NSLock alloc] init];
    self.lock.name = AFURLSessionManagerLockName;
    // 置空task关联的代理
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {        
        for (NSURLSessionDataTask *task in dataTasks) {
            [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
        }
        for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
            [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
        }
        for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
            [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
        }
    }];
    return self;
}

首先说说这个方法是干什么用的:这个方法用来异步的获取当前session的所有未完成的task。其实讲道理来说在初始化中调用这个方法应该里面一个task都不会有。我们打断点去看,也确实如此,里面的数组都是空的。但是想想也知道,AF大神不会把一段没用的代码放在这吧。辗转多处,终于从AF的issue中找到了结论:github

初始化方法到这就全部完成了。

分割图.png

接着我们来看看网络请求:

- (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
{
     //生成一个task
    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                        URLString:URLString
                                                       parameters:parameters
                                                   uploadProgress:nil
                                                 downloadProgress:downloadProgress
                                                          success:success
                                                          failure:failure];
    //开始网络请求
    [dataTask resume];
    return dataTask;
}

方法走到类AFHTTPSessionManager中来,调用父类,也就是我们整个AF3.x的核心类AFURLSessionManager的方法,生成了一个系 统的NSURLSessionDataTask实例,并且开始网络请求。
我们继续往父类里看,看看这个方法到底做了什么:

- (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;
    //把参数,还有各种东西转化为一个request
    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;
}

这里是用来忽略:?带来的警告,具体的各种编译器警告描述,可以参考这篇:各种编译器的警告

接着我们跑到AFURLRequestSerialization类中:

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(id)parameters
                                     error:(NSError *__autoreleasing *)error
{
    //断言,debug模式下,如果缺少改参数,crash
    NSParameterAssert(method);
    NSParameterAssert(URLString);
    NSURL *url = [NSURL URLWithString:URLString];
    NSParameterAssert(url);
    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
    mutableRequest.HTTPMethod = method;
    //将request的各种属性循环遍历
    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        //如果自己观察到的发生变化的属性,在这些方法里
        if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
           //把给自己设置的属性给request设置
            [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
        }
    }
    //将传入的parameters进行编码,并添加到request中
    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
    return mutableRequest;
}
static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
      static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
      static dispatch_once_t onceToken;
      // 此处需要observer的keypath为allowsCellularAccess、cachePolicy、HTTPShouldHandleCookies
      // HTTPShouldUsePipelining、networkServiceType、timeoutInterval
      dispatch_once(&onceToken, ^{
          _AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))];
      });
      //就是一个数组里装了很多方法的名字,
      return _AFHTTPRequestSerializerObservedKeyPaths;
    }

其实这个函数就是封装了一些属性的名字,这些都是NSUrlRequest的属性。
再来看看self.mutableObservedChangedKeyPaths,这个是当前类的一个属性:

@property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths;

在-init方法对这个集合进行了初始化,并且对当前类的和NSUrlRequest相关的那些属性添加了KVO监听

//每次都会重置变化
  self.mutableObservedChangedKeyPaths = [NSMutableSet set];
  //给这自己些方法添加观察者为自己,就是request的各种属性,set方法
  for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
      if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
          [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
      }
  }

KVO触发的方法:

-(void)observeValueForKeyPath:(NSString *)keyPath
                    ofObject:(__unused id)object
                      change:(NSDictionary *)change
                     context:(void *)context
{
  //当观察到这些set方法被调用了,而且不为Null就会添加到集合里,否则移除
  if (context == AFHTTPRequestSerializerObserverContext) {
      if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
          [self.mutableObservedChangedKeyPaths removeObject:keyPath];
      } else {
          [self.mutableObservedChangedKeyPaths addObject:keyPath];
      }
  }
}

至此我们知道self.mutableObservedChangedKeyPaths其实就是我们自己设置的request属性值的集合。

接下来调用:

[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];

用KVC的方式,把属性值都设置到我们请求的request中去。

3)把需要传递的参数进行编码,并且设置到request中去:

//将传入的parameters进行编码,并添加到request中
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                             withParameters:(id)parameters
                                      error:(NSError *__autoreleasing *)error
{
  NSParameterAssert(request);
  NSMutableURLRequest *mutableRequest = [request mutableCopy];
  //从自己的head里去遍历,如果有值则设置给request的head
  [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
      if (![request valueForHTTPHeaderField:field]) {
          [mutableRequest setValue:value forHTTPHeaderField:field];
      }
  }];
  //来把各种类型的参数,array dic set转化成字符串,给request
  NSString *query = nil;
  if (parameters) {
      //自定义的解析方式
      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;
          }
      }
  }
  //最后判断该request中是否包含了GET、HEAD、DELETE(都包含在HTTPMethodsEncodingParametersInURI)。因为这几个method的quey是拼接到url后面的。而POST、PUT是把query拼接到http body中的。
  if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
      if (query && query.length > 0) {
          mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
      }
  } else {
      //post put请求
      // #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;
}

这个方法做了3件事:
1.从self.HTTPRequestHeaders中拿到设置的参数,赋值要请求的request里去
2.把请求网络的参数,从array dic set这些容器类型转换为字符串,具体转码方式,我们可以使用自定义的方式,也可以用AF默认的转码方式。自定义的方式没什么好说的,想怎么去解析由你自己来决定。我们可以来看看默认的方式:

NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
  NSMutableArray *mutablePairs = [NSMutableArray array];
  //把参数给AFQueryStringPairsFromDictionary,拿到AF的一个类型的数据就一个key,value对象,在URLEncodedStringValue拼接keyValue,一个加到数组里
  for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
      [mutablePairs addObject:[pair URLEncodedStringValue]];
  }
  //拆分数组返回参数字符串
  return [mutablePairs componentsJoinedByString:@"&"];
}

NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
  //往下调用
  return AFQueryStringPairsFromKeyAndValue(nil, dictionary);
}

NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
  NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
  // 根据需要排列的对象的description来进行升序排列,并且selector使用的是compare:
  // 因为对象的description返回的是NSString,所以此处compare:使用的是NSString的compare函数
  // 即@[@"foo", @"bar", @"bae"] ----> @[@"bae", @"bar",@"foo"]
  NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
  //判断vaLue是什么类型的,然后去递归调用自己,直到解析的是除了array dic set以外的元素,然后把得到的参数数组返回。
  if ([value isKindOfClass:[NSDictionary class]]) {
      NSDictionary *dictionary = value;
      // Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
      //拿到
      for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
          id nestedValue = dictionary[nestedKey];
          if (nestedValue) {
              [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
          }
      }
  } else if ([value isKindOfClass:[NSArray class]]) {
      NSArray *array = value;
      for (id nestedValue in array) {
          [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
      }
  } else if ([value isKindOfClass:[NSSet class]]) {
      NSSet *set = value;
      for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
          [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
      }
  } else {
      [mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
  }
  return mutableQueryStringComponents;
}
@property (readwrite, nonatomic, strong) id field;
@property (readwrite, nonatomic, strong) id value;

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

方法很简单,现在我们也很容易理解这整个转码过程了,我们举个例子梳理下,就是以下这3步:

@{ 
     @"name" : @"bang", 
     @"phone": @{@"mobile": @"xx", @"home": @"xx"}, 
     @"families": @[@"father", @"mother"], 
     @"nums": [NSSet setWithObjects:@"1", @"2", nil] 
    } 
    -> 
    @[ 
     field: @"name", value: @"bang", 
     field: @"phone[mobile]", value: @"xx", 
     field: @"phone[home]", value: @"xx", 
     field: @"families[]", value: @"father", 
     field: @"families[]", value: @"mother", 
     field: @"nums", value: @"1", 
     field: @"nums", value: @"2", 
    ] 
    -> 
    name=bang&phone[mobile]=xx&phone[home]=xx&families[]=father&families[]=mother&nums=1&num=2

至此,我们原来的容器类型的参数,就这样变成字符串类型了。

紧接着这个方法还根据该request中请求类型,来判断参数字符串应该如何设置到request中去。如果是GET、HEAD、DELETE,则把参数quey是拼 接到url后面的。而POST、PUT是把query拼接到http body中的:

if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
  if (query && query.length > 0) {
      mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
  }
} else {
  //post put请求
  // #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]];
}

至此,我们生成了一个request。

分割图.png

我们再回到AFHTTPSessionManager类中来,回到这个方法:
- (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;
    //把参数,还有各种东西转化为一个request
    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;
}

绕了一圈我们又回来了。。

言归正传,我们接着调用了父类的生成task的方法,并且执行了一个成功和失败的回调,我们接着去父类AFURLSessionManger里看(总算到我们的核心类 了..):

- (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;
    //第一件事,创建NSURLSessionDataTask,里面适配了Ios8以下taskIdentifiers,函数创建task对象。
    //其实现应该是因为iOS 8.0以下版本中会并发地创建多个task对象,而同步有没有做好,导致taskIdentifiers 不唯一…这边做了一个串行处理
    url_session_manager_create_task_safely(^{
        dataTask = [self.session dataTaskWithRequest:request];
    });
    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
    return dataTask;
}

我们接着看到:

[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
{
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
    // AFURLSessionManagerTaskDelegate与AFURLSessionManager建立相互关系
    delegate.manager = self;
    delegate.completionHandler = completionHandler;
    //这个taskDescriptionForSessionTasks用来发送开始和挂起通知的时候会用到,就是用这个值来Post通知,来两者对应
    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    // ***** 将AF delegate对象与 dataTask建立关系
    [self setDelegate:delegate forTask:dataTask];
    // 设置AF delegate的上传进度,下载进度块。
    delegate.uploadProgressBlock = uploadProgressBlock;
    delegate.downloadProgressBlock = downloadProgressBlock;
}
@property (nonatomic, weak) AFURLSessionManager *manager;

这个属性是弱引用的,所以不会存在循环引用的问题。
3)我们调用了[self setDelegate:delegate forTask:dataTask];

我们进去看看这个方法做了什么:

- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    //断言,如果没有这个参数,debug下crash在这
    NSParameterAssert(task);
    NSParameterAssert(delegate);
    //加锁保证字典线程安全
    [self.lock lock];
    // 将AF delegate放入以taskIdentifier标记的词典中(同一个NSURLSession中的taskIdentifier是唯一的)
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    // 为AF delegate 设置task 的progress监听
    [delegate setupProgressForTask:task];
    //添加task开始和暂停的通知
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}
- (void)setupProgressForTask:(NSURLSessionTask *)task {
    __weak __typeof__(task) weakTask = task;
    //拿到上传下载期望的数据大小
    self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
    self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
    //将上传与下载进度和 任务绑定在一起,直接cancel suspend resume进度条,可以cancel...任务
    [self.uploadProgress setCancellable:YES];
    [self.uploadProgress setCancellationHandler:^{
        __typeof__(weakTask) strongTask = weakTask;
        [strongTask cancel];
    }];
    [self.uploadProgress setPausable:YES];
    [self.uploadProgress setPausingHandler:^{
        __typeof__(weakTask) strongTask = weakTask;
        [strongTask suspend];
    }];
    if ([self.uploadProgress respondsToSelector:@selector(setResumingHandler:)]) {
        [self.uploadProgress setResumingHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask resume];
        }];
    }
    [self.downloadProgress setCancellable:YES];
    [self.downloadProgress setCancellationHandler:^{
        __typeof__(weakTask) strongTask = weakTask;
        [strongTask cancel];
    }];
    [self.downloadProgress setPausable:YES];
    [self.downloadProgress setPausingHandler:^{
        __typeof__(weakTask) strongTask = weakTask;
        [strongTask suspend];
    }];
    if ([self.downloadProgress respondsToSelector:@selector(setResumingHandler:)]) {
        [self.downloadProgress setResumingHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask resume];
        }];
    }
    //观察task的这些属性
    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
              options:NSKeyValueObservingOptionNew
              context:NULL];
    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))
              options:NSKeyValueObservingOptionNew
              context:NULL];
    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))
              options:NSKeyValueObservingOptionNew
              context:NULL];
    [task addObserver:self
           forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))
              options:NSKeyValueObservingOptionNew
              context:NULL];
    //观察progress这两个属性
    [self.downloadProgress addObserver:self
                            forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                               options:NSKeyValueObservingOptionNew
                               context:NULL];
    [self.uploadProgress addObserver:self
                          forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                             options:NSKeyValueObservingOptionNew
                             context:NULL];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
  //是task
  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];
      }
  }
  //上面的赋新值会触发这两个,调用block回调,用户拿到进度
  else if ([object isEqual:self.downloadProgress]) {
      if (self.downloadProgressBlock) {
          self.downloadProgressBlock(object);
      }
  }
  else if ([object isEqual:self.uploadProgress]) {
      if (self.uploadProgressBlock) {
          self.uploadProgressBlock(object);
      }
  }
}

到这里我们整个对task的处理就完成了。

分割图.png

接着task就开始请求网络了,还记得我们初始化方法中:

self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];

我们把AFUrlSessionManager作为了所有的task的delegate。当我们请求网络的时候,这些代理开始调用了:

NSUrlSession的代理.png

AF自定义delegate.png

这就是我们一开始说的,AFUrlSessionManager对这一大堆代理做了一些公共的处理,而转发到AF自定义代理的3条,则负责把每个task对应的数据回 调出去。

又有小伙伴问了,我们设置的这个代理不是NSURLSessionDelegate吗?怎么能响应NSUrlSession这么多代理呢?我们点到类的声明文件中 去看看:

@protocol NSURLSessionDelegate <NSObject>
@protocol NSURLSessionTaskDelegate <NSURLSessionDelegate>
@protocol NSURLSessionDataDelegate <NSURLSessionTaskDelegate>
@protocol NSURLSessionDownloadDelegate <NSURLSessionTaskDelegate>
@protocol NSURLSessionStreamDelegate <NSURLSessionTaskDelegate>

这样如果没实现这些我们自定义的Block也不会去回调这些代理。因为本身某些代理,只执行了这些自定义的Block,如果Block都没有赋值,那我们调用代理也没 有任何意义。

讲到这,我们顺便看看AFUrlSessionManager的一些自定义Block:

@property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid;
@property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge;
@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession;
@property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge;
@property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume;

各自对应的还有一堆这样的set方法:

- (void)setSessionDidBecomeInvalidBlock:(void (^)(NSURLSession *session, NSError *error))block {
  self.sessionDidBecomeInvalid = block;
}

方法都是一样的,就不重复粘贴占篇幅了。
主要谈谈这个设计思路

为什么要绕这么一大圈呢?原来这是为了我们这些用户使用起来方便,调用set方法去设置这些Block,能很清晰的看到Block的各个参数与返回值。大神的 精髓的编程思想无处不体现...

接下来我们就讲讲这些代理方法做了什么(按照顺序来):

NSURLSessionDelegate
代理1:
//当前这个session已经失效时,该代理方法被调用。
/*
 如果你使用finishTasksAndInvalidate函数使该session失效,
 那么session首先会先完成最后一个task,然后再调用URLSession:didBecomeInvalidWithError:代理方法,
 如果你调用invalidateAndCancel方法来使session失效,那么该session会立即调用上面的代理方法。
 */
- (void)URLSession:(NSURLSession *)session
didBecomeInvalidWithError:(NSError *)error
{
    if (self.sessionDidBecomeInvalid) {
        self.sessionDidBecomeInvalid(session, error);
    }
    [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
}
代理2:
//2、https认证
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
    //挑战处理类型为 默认
    /*
     NSURLSessionAuthChallengePerformDefaultHandling:默认方式处理
     NSURLSessionAuthChallengeUseCredential:使用指定的证书
     NSURLSessionAuthChallengeCancelAuthenticationChallenge:取消挑战
     */
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    __block NSURLCredential *credential = nil;
    // sessionDidReceiveAuthenticationChallenge是自定义方法,用来如何应对服务器端的认证挑战
    if (self.sessionDidReceiveAuthenticationChallenge) {
        disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
    } else {
        // 此处服务器要求客户端的接收认证挑战方法是NSURLAuthenticationMethodServerTrust
        // 也就是说服务器端需要客户端返回一个根据认证挑战的保护空间提供的信任(即challenge.protectionSpace.serverTrust)产生的挑战证书。
        // 而这个证书就需要使用credentialForTrust:来创建一个NSURLCredential对象
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            // 基于客户端的安全策略来决定是否信任该服务器,不信任的话,也就没必要响应挑战
            if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
                // 创建挑战证书(注:挑战方式为UseCredential和PerformDefaultHandling都需要新建挑战证书)
                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                // 确定挑战的方式
                if (credential) {
                    //证书挑战
                    disposition = NSURLSessionAuthChallengeUseCredential;
                } else {
                    //默认挑战  唯一区别,下面少了这一步!
                    disposition = NSURLSessionAuthChallengePerformDefaultHandling;
                }
            } else {
                //取消挑战
                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
            }
        } else {
            //默认挑战方式
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }
    }
    //完成挑战
    if (completionHandler) {
        completionHandler(disposition, credential);
    }
}

这里,我把官方文档对这个方法的描述翻译了一下。
总结一下,这个方法其实就是做https认证的。看看上面的注释,大概能看明白这个方法做认证的步骤,我们还是如果有自定义的做认证的Block,则调用我们自定义的,否则去执行默认的认证步骤,最后调用完成认证:

//完成挑战 
if (completionHandler) { 
      completionHandler(disposition, credential); 
}
代理3:
//3、 当session中所有已经入队的消息被发送出去后,会调用该代理方法。
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
    if (self.didFinishEventsForBackgroundURLSession) {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.didFinishEventsForBackgroundURLSession(session);
        });
    }
}

官方文档翻译:

函数讨论:

NSURLSessionTaskDelegate
代理4:
//被服务器重定向的时候调用
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
        newRequest:(NSURLRequest *)request
 completionHandler:(void (^)(NSURLRequest *))completionHandler
{
    NSURLRequest *redirectRequest = request;
    // step1. 看是否有对应的user block 有的话转发出去,通过这4个参数,返回一个NSURLRequest类型参数,request转发、网络重定向.
    if (self.taskWillPerformHTTPRedirection) {
        //用自己自定义的一个重定向的block实现,返回一个新的request。
        redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request);
    }
    if (completionHandler) {
        // step2. 用request重新请求
        completionHandler(redirectRequest);
    }
}
<?php
    defined('BASEPATH') OR exit('No direct script access allowed');
    class Welcome extends CI_Controller {
        public function index()
        {
            header("location: http://www.huixionghome.cn/");
        }
    }

证实确实如此,当我们服务器重定向的时候,代理就被调用了,我们可以去重新定义这个重定向的request。

此方法只会在default session或者ephemeral session中调用,而在background session中,sessiontask会自动重定向。

这里指的模式是我们一开始Init的模式:

if (!configuration) {
    configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.sessionConfiguration = configuration;

这个模式总共分为3种:

对于NSURLSession对象的初始化需要使用NSURLSessionConfiguration,而NSURLSessionConfiguration 有三个类工厂方法:
+defaultSessionConfiguration 返回一个标准的 configuration,这个配置实际上与 NSURLConnection的网络堆栈(networking stack)是一样的,具有相同的共享 NSHTTPCookieStorage,共享 NSURLCache和共享NSURLCredentialStorage。
+ephemeralSessionConfiguration 返回一个预设配置,这个配置中不会对缓存,Cookie和证书进行持久性的存储。这对于实现像秘密浏览这种功能来说是很理想的。
+backgroundSessionConfiguration:(NSString *)identifier 的独特之处在于,它会创建一个后台session。后台 session 不同于常规的,普通的 session,它甚至可以在应用程序挂起,退出或者崩溃的情况下运行上传和下载任务。初始化时指定的标识符,被用于向任何可能在进程外恢复后台传输的守护进程(daemon)提供上下文。

代理5:
//https认证
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    __block NSURLCredential *credential = nil;
    if (self.taskDidReceiveAuthenticationChallenge) {
        disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential);
    } else {
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
                disposition = NSURLSessionAuthChallengeUseCredential;
                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            } else {
                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
            }
        } else {
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }
    }
    if (completionHandler) {
        completionHandler(disposition, credential);
    }
}
代理6:
//当一个session task需要发送一个新的request body stream到服务器端的时候,调用该代理方法。
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
 needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
{
    NSInputStream *inputStream = nil;
    //有自定义的taskNeedNewBodyStream,用自定义的,不然用task里原始的stream
    if (self.taskNeedNewBodyStream) {
        inputStream = self.taskNeedNewBodyStream(session, task);
    } else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) {
        inputStream = [task.originalRequest.HTTPBodyStream copy];
    }
    if (completionHandler) {
        completionHandler(inputStream);
    }
}
代理7:
/*
 //周期性地通知代理发送到服务器端数据的进度。
 */
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
   didSendBodyData:(int64_t)bytesSent
    totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
     // 如果totalUnitCount获取失败,就使用HTTP header中的Content-Length作为totalUnitCount
    int64_t totalUnitCount = totalBytesExpectedToSend;
    if(totalUnitCount == NSURLSessionTransferSizeUnknown) {
        NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"];
        if(contentLength) {
            totalUnitCount = (int64_t) [contentLength longLongValue];
        }
    }
    if (self.taskDidSendBodyData) {
        self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
    }
}
未完总结:

后续文章:
AFNetworking到底做了什么(二)?
AFNetworking之于https认证
AFNetworking之UIKit扩展与缓存实现
AFNetworking到底做了什么?(终)


原文出处:AFNetworking到底做了什么?(二)

AFNetworking到底做了什么?(二)

接着上一篇的内容往下讲,如果没看过上一篇内容可以点这:

AFNetworking到底做了什么?

之前我们讲到NSUrlSession代理这一块:

代理8:
/*
 task完成之后的回调,成功和失败都会回调这里
 函数讨论:
 注意这里的error不会报告服务期端的error,他表示的是客户端这边的eroor,比如无法解析hostname或者连不上host主机。
 */
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{   
    //根据task去取我们一开始创建绑定的delegate
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
    // delegate may be nil when completing a task in the background
    if (delegate) {
        //把代理转发给我们绑定的delegate
        [delegate URLSession:session task:task didCompleteWithError:error];
        //转发完移除delegate
        [self removeDelegateForTask:task];
    }
    //自定义Block回调
    if (self.taskDidComplete) {
        self.taskDidComplete(session, task, error);
    }  
}

这个代理就是task完成了的回调,方法内做了下面这几件事:

NSURLSessionDataDelegate:
代理9:
//收到服务器响应后调用
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
    //设置默认为继续进行
    NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;
    //自定义去设置
    if (self.dataTaskDidReceiveResponse) {
        disposition = self.dataTaskDidReceiveResponse(session, dataTask, response);
    }
    if (completionHandler) {
        completionHandler(disposition);
    }
}

官方文档翻译如下:

函数作用:
告诉代理,该data task获取到了服务器端传回的最初始回复(response)。注意其中的completionHandler这个block,通过传入一个 类型为NSURLSessionResponseDisposition的变量来决定该传输任务接下来该做什么:
NSURLSessionResponseAllow 该task正常进行
NSURLSessionResponseCancel 该task会被取消
NSURLSessionResponseBecomeDownload 会调用URLSession:dataTask:didBecomeDownloadTask:方法来新建一个download task以代替当前的data task
NSURLSessionResponseBecomeStream 转成一个StreamTask

函数讨论:
该方法是可选的,除非你必须支持“multipart/x-mixed-replace”类型的content-type 。因为如果你的request中包含了这种类型的content-type,服务器会将数据分片传回来,而且每次传回来的数据会覆盖之前的数据。每次返回新的数据时, session都会调用该函数,你应该在这个函数中合理地处理先前的数据,否则会被新数据覆盖。如果你没有提供该方法的实现,那么session将会继续任务,也就是 说会覆盖之前的数据。

总结一下:

代理10:
//上面的代理如果设置为NSURLSessionResponseBecomeDownload,则会调用这个方法
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
{
    //因为转变了task,所以要对task做一个重新绑定
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
    if (delegate) {
        [self removeDelegateForTask:dataTask];
        [self setDelegate:delegate forTask:downloadTask];
    }
    //执行自定义Block
    if (self.dataTaskDidBecomeDownloadTask) {
        self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);
    }
}

按照顺序来,其实还有个AF没有去实现的代理:

//AF没实现的代理
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didBecomeStreamTask:(NSURLSessionStreamTask *)streamTask;

这个也是之前的那个代理,设置为NSURLSessionResponseBecomeStream则会调用到这个代理里来。会新生成一个NSURLSessi onStreamTask来替换掉之前的dataTask。

代理11:
//当我们获取到数据就会调用,会被反复调用,请求到的数据就在这被拼装完整
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data
{
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
    [delegate URLSession:session dataTask:dataTask didReceiveData:data];
    if (self.dataTaskDidReceiveData) {
        self.dataTaskDidReceiveData(session, dataTask, data);
    }
}
代理12:
/*当task接收到所有期望的数据后,session会调用此代理方法。
*/
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
 willCacheResponse:(NSCachedURLResponse *)proposedResponse
 completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler
{
    NSCachedURLResponse *cachedResponse = proposedResponse;
    if (self.dataTaskWillCacheResponse) {
        cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse);
    }
    if (completionHandler) {
        completionHandler(cachedResponse);
    }
}

官方文档翻译如下:

函数作用:
询问data task或上传任务(upload task)是否缓存response。

函数讨论:
当task接收到所有期望的数据后,session会调用此代理方法。如果你没有实现该方法,那么就会使用创建session时使用的configuration对象 决定缓存策略。这个代理方法最初的目的是为了阻止缓存特定的URLs或者修改NSCacheURLResponse对象相关的userInfo字典。
该方法只会当request决定缓存response时候调用。作为准则,responses只会当以下条件都成立的时候返回缓存:
该request是HTTP或HTTPS URL的请求(或者你自定义的网络协议,并且确保该协议支持缓存)
确保request请求是成功的(返回的status code为200-299)
返回的response是来自服务器端的,而非缓存中本身就有的
提供的NSURLRequest对象的缓存策略要允许进行缓存
服务器返回的response中与缓存相关的header要允许缓存
该response的大小不能比提供的缓存空间大太多(比如你提供了一个磁盘缓存,那么response大小一定不能比磁盘缓存空间还要大5%)

NSURLSessionDownloadDelegate
代理13:
//下载完成的时候调用
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
    //这个是session的,也就是全局的,后面的个人代理也会做同样的这件事
    if (self.downloadTaskDidFinishDownloading) {
        //调用自定义的block拿到文件存储的地址
        NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
        if (fileURL) {
            delegate.downloadFileURL = fileURL;
            NSError *error = nil;
            //从临时的下载路径移动至我们需要的路径
            [[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error];
            //如果移动出错
            if (error) {
                [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
            }
            return;
        }
    }
    //转发代理
    if (delegate) {
        [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
    }
}

总共就这3个方法,被转调到AF自定义delegate中。

代理14:
//周期性地通知下载进度调用
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    if (self.downloadTaskDidWriteData) {
        self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
    }
}

简单说一下这几个参数:
bytesWritten 表示自上次调用该方法后,接收到的数据字节数
totalBytesWritten表示目前已经接收到的数据字节数
totalBytesExpectedToWrite 表示期望收到的文件总字节数,是由Content-Length header提供。如果没有提供,默认是NSURLSessionTransferSizeUnknown。

代理15:
//当下载被取消或者失败后重新恢复下载时调用
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
 didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes
{
    //交给自定义的Block去调用
    if (self.downloadTaskDidResume) {
        self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes);
    }
}

官方文档翻译:

函数作用:
告诉代理,下载任务重新开始下载了。

函数讨论:
如果一个正在下载任务被取消或者失败了,你可以请求一个resumeData对象(比如在userInfo字典中通过NSURLSessionDownloadTas kResumeData这个键来获取到resumeData)并使用它来提供足够的信息以重新开始下载任务。
随后,你可以使用resumeData作为downloadTaskWithResumeData:或downloadTaskWithResumeData:com pletionHandler:的参数。当你调用这些方法时,你将开始一个新的下载任务。一旦你继续下载任务,session会调用它的代理方法URLSession :downloadTask:didResumeAtOffset:expectedTotalBytes:其中的downloadTask参数表示的就是新的下载任 务,这也意味着下载重新开始了。

总结一下:

至此NSUrlSesssion的delegate讲完了。大概总结下:

分割图.png

接下来我们来看转发到AF的deleagate,一共3个方法:

AF代理1:
//AF实现的代理!被从urlsession那转发到这
- (void)URLSession:(__unused NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
    //1)强引用self.manager,防止被提前释放;因为self.manager声明为weak,类似Block
    __strong AFURLSessionManager *manager = self.manager;
    __block id responseObject = nil;
    //用来存储一些相关信息,来发送通知用的
    __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    //存储responseSerializer响应解析对象
    userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
    //Performance Improvement from #2672
    //注意这行代码的用法,感觉写的很Nice...把请求到的数据data传出去,然后就不要这个值了释放内存
    NSData *data = nil;
    if (self.mutableData) {
        data = [self.mutableData copy];
        //We no longer need the reference, so nil it out to gain back some memory.
        self.mutableData = nil;
    }
    //继续给userinfo填数据
    if (self.downloadFileURL) {
        userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
    } else if (data) {
        userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
    }
    //错误处理
    if (error) {
        userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
        //可以自己自定义完成组 和自定义完成queue,完成回调
        dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
            if (self.completionHandler) {
                self.completionHandler(task.response, responseObject, error);
            }
            //主线程中发送完成通知
            dispatch_async(dispatch_get_main_queue(), ^{
                [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
            });
        });
    } else {
        //url_session_manager_processing_queue AF的并行队列
        dispatch_async(url_session_manager_processing_queue(), ^{
            NSError *serializationError = nil;
            //解析数据
            responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
            //如果是下载文件,那么responseObject为下载的路径
            if (self.downloadFileURL) {
                responseObject = self.downloadFileURL;
            }
            //写入userInfo
            if (responseObject) {
                userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
            }
            //如果解析错误
            if (serializationError) {
                userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
            }
            //回调结果
            dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
                if (self.completionHandler) {
                    self.completionHandler(task.response, responseObject, serializationError);
                }
                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
                });
            });
        });
    }
#pragma clang diagnostic pop
}

这个方法是NSUrlSession任务完成的代理方法中,主动调用过来的。配合注释,应该代码很容易读,这个方法大概做了以下几件事:

  1. 生成了一个存储这个task相关信息的字典:userInfo,这个字典是用来作为发送任务完成的通知的参数。
  2. 判断了参数error的值,来区分请求成功还是失败。
  3. 如果成功则在一个AF的并行queue中,去做数据解析等后续操作:

    static dispatch_queue_t url_session_manager_processing_queue() {

    static dispatch_queue_t af_url_session_manager_processing_queue;
     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
         af_url_session_manager_processing_queue = dispatch_queue_create("com.alamofire.networking.session.manager.processing", DISPATCH_QUEUE_CONCURRENT);
     });
     return af_url_session_manager_processing_queue;
    

    }

注意AF的优化的点,虽然代理回调是串行的(不明白可以见本文最后)。但是数据解析这种费时操作,确是用并行线程来做的。

  1. 然后根据我们一开始设置的responseSerializer来解析data。如果解析成功,调用成功的回调,否则调用失败的回调。
    我们重点来看看返回数据解析这行:
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];

我们点进去看看:

@protocol AFURLResponseSerialization <NSObject, NSSecureCoding, NSCopying>
- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
                         data:(nullable NSData *)data
                        error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
@end

原来就是这么一个协议方法,各种类型的responseSerializer类,都是遵守这个协议方法,实现了一个把我们请求到的data转换为我们需要的类型的数据的方法。至于各种类型的responseSerializer如何解析数据,我们到代理讲完再来补充。

  1. 这边还做了一个判断,如果自定义了GCD完成组completionGroup和完成队列的话completionQueue,会在加入这个组和在队列中回调Block。否则默认的是AF的创建的组:

    static dispatch_group_t url_session_manager_completion_group() {

    static dispatch_group_t af_url_session_manager_completion_group;
     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
         af_url_session_manager_completion_group = dispatch_group_create();
     });
     return af_url_session_manager_completion_group;
    

    }

和主队列回调。AF没有用这个GCD组做任何处理,只是提供这个接口,让我们有需求的自行调用处理。如果有对多个任务完成度的监听,可以自行处理。
而队列的话,如果你不需要回调主线程,可以自己设置一个回调队列。

  1. 回到主线程,发送了任务完成的通知:

    dispatch_async(dispatch_get_main_queue(), ^{

    [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
         });
    

这个通知这回AF有用到了,在我们对UIKit的扩展中,用到了这个通知。

AF代理2:
- (void)URLSession:(__unused NSURLSession *)session
          dataTask:(__unused NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data
{
    //拼接数据
    [self.mutableData appendData:data];
}

同样被NSUrlSession代理转发到这里,拼接了需要回调的数据。

AF代理3:
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
    NSError *fileManagerError = nil;
    self.downloadFileURL = nil;
    //AF代理的自定义Block
    if (self.downloadTaskDidFinishDownloading) {
        //得到自定义下载路径
        self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
        if (self.downloadFileURL) {
            //把下载路径移动到我们自定义的下载路径
            [[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError];
            //错误发通知
            if (fileManagerError) {
                [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
            }
        }
    }
}

下载成功了被NSUrlSession代理转发到这里,这里有个地方需要注意下:

这个地方return的path就是对应的这个代理方法里的path,我们调用最终会走到这么一个方法:

- (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask
                        progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                     destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
               completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
  AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
  delegate.manager = self;
  delegate.completionHandler = completionHandler;
  //返回地址的Block
  if (destination) {
      //有点绕,就是把一个block赋值给我们代理的downloadTaskDidFinishDownloading,这个Block里的内部返回也是调用Block去获取到的,这里面的参数都是AF代理传过去的。
      delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
          //把Block返回的地址返回
          return destination(location, task.response);
      };
  }
  downloadTask.taskDescription = self.taskDescriptionForSessionTasks;
  [self setDelegate:delegate forTask:downloadTask];
  delegate.downloadProgressBlock = downloadProgressBlock;
}

清楚的可以看到地址被赋值给AF的Block了。

至此AF的代理也讲完了,数据或错误信息随着AF代理成功失败回调,回到了用户的手中。

分割图.png

接下来我们来补充之前AFURLResponseSerialization这一块是如何解析数据的:

AFURLResponseSerialization.png

如图所示,AF用来解析数据的一共上述这些方法。第一个实际是一个协议方法,协议方法如下:

@protocol AFURLResponseSerialization <NSObject, NSSecureCoding, NSCopying>
- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
                           data:(nullable NSData *)data
                          error:(NSError * _Nullable __autoreleasing *)error;
@end

而后面6个类都是遵守这个协议方法,去做数据解析。这地方可以再次感受一下AF的设计模式...接下来我们就来主要看看这些类对这个协议方法的实现:

AFHTTPResponseSerializer:
- (id)responseObjectForResponse:(NSURLResponse *)response
                           data:(NSData *)data
                          error:(NSError *__autoreleasing *)error
{
    [self validateResponse:(NSHTTPURLResponse *)response data:data error:error];
    return data;
}
// 判断是不是可接受类型和可接受code,不是则填充error
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
                    data:(NSData *)data
                   error:(NSError * __autoreleasing *)error
{
    //response是否合法标识
    BOOL responseIsValid = YES;
    //验证的error
    NSError *validationError = nil;
    //如果存在且是NSHTTPURLResponse
    if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
        //主要判断自己能接受的数据类型和response的数据类型是否匹配,
        //如果有接受数据类型,如果不匹配response,而且响应类型不为空,数据长度不为0
        if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] &&
            !([response MIMEType] == nil && [data length] == 0)) {
            //进入If块说明解析数据肯定是失败的,这时候要把解析错误信息放到error里。
            //如果数据长度大于0,而且有响应url
            if ([data length] > 0 && [response URL]) {
                //错误信息字典,填充一些错误信息
                NSMutableDictionary *mutableUserInfo = [@{
                                                          NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]],
                                                          NSURLErrorFailingURLErrorKey:[response URL],
                                                          AFNetworkingOperationFailingURLResponseErrorKey: response,
                                                        } mutableCopy];
                if (data) {
                    mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
                }
                //生成错误
                validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
            }
            //返回标识
            responseIsValid = NO;
        }
        //判断自己可接受的状态吗
        //如果和response的状态码不匹配,则进入if块
        if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
            //填写错误信息字典
            NSMutableDictionary *mutableUserInfo = [@{
                                               NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
                                               NSURLErrorFailingURLErrorKey:[response URL],
                                               AFNetworkingOperationFailingURLResponseErrorKey: response,
                                       } mutableCopy];
            if (data) {
                mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
            }
            //生成错误
            validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
            //返回标识
            responseIsValid = NO;
        }
    }
    //给我们传过来的错误指针赋值
    if (error && !responseIsValid) {
        *error = validationError;
    }
    //返回是否错误标识
    return responseIsValid;
}
AFJSONResponseSerializer:
- (id)responseObjectForResponse:(NSURLResponse *)response
                           data:(NSData *)data
                          error:(NSError *__autoreleasing *)error
{
    //先判断是不是可接受类型和可接受code
    if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
        //error为空,或者有错误,去函数里判断。
        if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
            //返回空
            return nil;
        }
    }
    id responseObject = nil;
    NSError *serializationError = nil;
    // Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization.
    // See https://github.com/rails/rails/issues/1742
    //如果数据为空
    BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
    //不空则去json解析
    if (data.length > 0 && !isSpace) {
        responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
    } else {
        return nil;
    }
    //判断是否需要移除Null值
    if (self.removesKeysWithNullValues && responseObject) {
        responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
    }
    //拿着json解析的error去填充错误信息
    if (error) {
        *error = AFErrorWithUnderlyingError(serializationError, *error);
    }
    //返回解析结果
    return responseObject;
}

注释写的很清楚,大概需要讲一下的是以下几个函数:

//1
AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain))
//2
AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
//3
AFErrorWithUnderlyingError(serializationError, *error);

之前注释已经写清楚了这些函数的作用,首先来看第1个:

//判断是不是我们自己之前生成的错误信息,是的话返回YES
static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) {
    //判断错误域名和传过来的域名是否一致,错误code是否一致
    if ([error.domain isEqualToString:domain] && error.code == code) {
        return YES;
    }
    //如果userInfo的NSUnderlyingErrorKey有值,则在判断一次。
    else if (error.userInfo[NSUnderlyingErrorKey]) {
        return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain);
    }
    return NO;
}

这里可以注意,我们这里传过去的code和domain两个参数分别为NSURLErrorCannotDecodeContentDataAFURLResponseSerializationErrorDomain,这两个参数是我们之前判断response可接受类型和code时候自己去生成错误的时候填写的。

第二个:

static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) {
    //分数组和字典
    if ([JSONObject isKindOfClass:[NSArray class]]) {
        //生成一个数组,只需要JSONObject.count个,感受到大神写代码的严谨态度了吗...
        NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]];
        for (id value in (NSArray *)JSONObject) {
            //调用自己
            [mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)];
        }
        //看我们解析类型是mutable还是非muatable,返回mutableArray或者array
        return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray];
    } else if ([JSONObject isKindOfClass:[NSDictionary class]]) {
        NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:JSONObject];
        for (id <NSCopying> key in [(NSDictionary *)JSONObject allKeys]) {
            id value = (NSDictionary *)JSONObject[key];
            //value空则移除
            if (!value || [value isEqual:[NSNull null]]) {
                [mutableDictionary removeObjectForKey:key];
            } else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {
                //如果数组还是去调用自己
                mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions);
            }
        }
        return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary];
    }
    return JSONObject;
}

方法主要还是通过递归的形式实现。比较简单。

第三个:

static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
    if (!error) {
        return underlyingError;
    }
    if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) {
        return error;
    }
    NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy];
    mutableUserInfo[NSUnderlyingErrorKey] = underlyingError;
    return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo];
}

方法主要是把json解析的错误,赋值给我们需要返回给用户的error上。比较简单,小伙伴们自己看看就好。

至此,AFJSONResponseSerializer就讲完了。
而我们ResponseSerialize还有一些其他的类型解析,大家可以自行去阅读,代码还是很容易读的,在这里就不浪费篇幅去讲了。

分割图.png

在AFURLSessionManager中,有这么一个类:_AFURLSessionTaskSwizzling。这个类大概的作用就是替换掉NSUrlS ession中的resumesuspend方法。正常处理原有逻辑的同时,多发送一个通知,以下是我们需要替换的新方法:

//被替换掉的方法,只要有TASK开启或者暂停,都会执行
- (void)af_resume {
    NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
    NSURLSessionTaskState state = [self state];
    [self af_resume];
    if (state != NSURLSessionTaskStateRunning) {
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
    }
}
- (void)af_suspend {
    NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
    NSURLSessionTaskState state = [self state];
    [self af_suspend];
    if (state != NSURLSessionTaskStateSuspended) {
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];
    }
}

这块知识是关于OC的Runtime:method swizzling的,如果有不清楚的地方,可以看看这里method swizzling--by冰霜或者自行查阅。

+ (void)load {
    if (NSClassFromString(@"NSURLSessionTask")) {
        // 1) 首先构建一个NSURLSession对象session,再通过session构建出一个_NSCFLocalDataTask变量
        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
        NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnonnull"
        NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
#pragma clang diagnostic pop
        // 2) 获取到af_resume实现的指针
        IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
        Class currentClass = [localDataTask class];
        // 3) 检查当前class是否实现了resume。如果实现了,继续第4步。
        while (class_getInstanceMethod(currentClass, @selector(resume))) {
            // 4) 获取到当前class的父类(superClass)
            Class superClass = [currentClass superclass];
            // 5) 获取到当前class对于resume实现的指针
            IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
            //  6) 获取到父类对于resume实现的指针
            IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
               // 7) 如果当前class对于resume的实现和父类不一样(类似iOS7上的情况),并且当前class的resume实现和af_resume不一样,才进行method swizzling。
            if (classResumeIMP != superclassResumeIMP &&
                originalAFResumeIMP != classResumeIMP) {
                //执行交换的函数
                [self swizzleResumeAndSuspendMethodForClass:currentClass];
            }
            // 8) 设置当前操作的class为其父类class,重复步骤3~8
            currentClass = [currentClass superclass];
        }
        [localDataTask cancel];
        [session finishTasksAndInvalidate];
    }
}

原方法中有大量的英文注释,我把它翻译过来如下:

iOS 7和iOS 8在NSURLSessionTask实现上有些许不同,这使得下面的代码实现略显trick关于这个问题,大家做了很多Unit Test,足以证明这个方法是可行的
目前我们所知的:

一些假设前提:

其余的一部分翻译在注释中,对应那一行代码。大概总结下这个注释:

但是有几个点大家可能会觉得疑惑的,我们先把这个方法调用的替换的函数一块贴出来。

//其引用的交换的函数:
+ (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass {
    Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume));
    Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend));
    if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) {
        af_swizzleSelector(theClass, @selector(resume), @selector(af_resume));
    }
    if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) {
        af_swizzleSelector(theClass, @selector(suspend), @selector(af_suspend));
    }
}
static inline void af_swizzleSelector(Class theClass, SEL originalSelector, SEL swizzledSelector) {
    Method originalMethod = class_getInstanceMethod(theClass, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(theClass, swizzledSelector);
    method_exchangeImplementations(originalMethod, swizzledMethod);
}
static inline BOOL af_addMethod(Class theClass, SEL selector, Method method) {
    return class_addMethod(theClass, selector,  method_getImplementation(method),  method_getTypeEncoding(method));
}

因为有小伙伴问到过,所以我们来分析分析大家可能会觉得疑惑的地方:

  1. 首先可以注意class_getInstanceMethod这个方法,它会获取到当前类继承链逐级往上,第一个实现的该方法。所以说它获取到的方法不能确定是当前类还是父类的。而且这里也没有用dispatch_once_t来保证一个方法只交换一次,那万一这是父类的方法,当前类换一次,父类又换一次,不是等于没交换么?...请注意这行判断:

    // 7) 如果当前class对于resume的实现和父类不一样(类似iOS7上的情况),并且当前class的resume实现和af_resume不一样,才进行method swizzling。 if (classResumeIMP != superclassResumeIMP && originalAFResumeIMP != classResumeIMP) {

    //执行交换的函数
        [self swizzleResumeAndSuspendMethodForClass:currentClass];
    

    }

这个条件就杜绝了这种情况的发生,只有当前类实现了这个方法,才可能进入这个if块。

2.那iOS7两个类都交换了af_resume,那岂不是父类换到子类方法了?...只能说又是没仔细看代码的...注意AF是去向当前类添加af_resume方法,然后去交换当前类的af_resume。所以说根本不会出现这种情况...

AFUrlSessionManager 基本上就这么多内容了。

分割图.png

现在我们回到一开始初始化的这行代码上:

self.operationQueue.maxConcurrentOperationCount = 1;

1)首先我们要明确一个概念,这里的并发数仅仅是回调代理的线程并发数。而不是请求网络的线程并发数。请求网络是由NSUrlSession来做的,它内部维护了一个线程池,用来做网络请求。它调度线程,基于底层的CFSocket去发送请求和接收数据。这些线程是并发的

2)明确了这个概念之后,我们来梳理一下AF3.x的整个流程和线程的关系:

3)最后我们来解释解释为什么回调Queue要设置并发数为1:

1)众所周知,AF2.x所有的回调是在一条线程,这条线程是AF的常驻线程,而这一条线程正是AF调度request的思想精髓所在,所以第一个目的就是为了和之前 版本保持一致。
2)因为跟代理相关的一些操作AF都使用了NSLock。所以就算Queue的并发数设置为n,因为多线程回调,锁的等待,导致所提升的程序速度也并不明显。反而多task回调导致的多线程并发,平白浪费了部分性能。
而设置Queue的并发数为1,(注:这里虽然回调Queue的并发数为1,仍然会有不止一条线程,但是因为是串行回调,所以同一时间,只会有一条线程在操作AFUrlSessionManager的那些方法。)至少回调的事件,是不需要多线程并发的。回调没有了NSLock的等待时间,所以对时间并没有多大的影响。(注:但是还是会有多线程的操作的,因为设置刚开始调起请求的时候,是在主线程的,而回调则是串行分线程。)

当然这仅仅是我个人的看法,如果有不同意见的欢迎交流~

至此我们AF3.X业务层的逻辑,基本上结束了。小伙伴们,看到这你明白了AF做了什么了吗?可能很多朋友要扔鸡蛋了...可能你还是没觉得AF到底有什么用,我用NSUrlSession不也一样,我干嘛要用AF,在这里,我暂时卖个关子,等我们下篇讲完AFSecurityPolicy和部分UIKit的扩展,以及A F2.x的核心类源码实现之后,我们再好好总结。

后续文章:

AFNetworking之于https认证
AFNetworking之UIKit扩展与缓存实现
AFNetworking到底做了什么?(终)