请选择 进入手机版 | 继续访问电脑版
接下来我们来看转发到AF的deleagate,一共3个方法:
AF代理1:
[Objective-C] 纯文本查看 复制代码
//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任务完成的代理方法中,主动调用过来的。配合注释,应该代码很容易读,这个方法大概做了以下几件事:
  • 生成了一个存储这个task相关信息的字典:userInfo,这个字典是用来作为发送任务完成的通知的参数。
  • 判断了参数error的值,来区分请求成功还是失败。
  • 如果成功则在一个AF的并行queue中,去做数据解析等后续操作:


[Objective-C] 纯文本查看 复制代码
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的优化的点,虽然代理回调是串行的(不明白可以见本文最后)。但是数据解析这种费时操作,确是用并行线程来做的。
  • 然后根据我们一开始设置的responseSerializer来解析data。如果解析成功,调用成功的回调,否则调用失败的回调。
    我们重点来看看返回数据解析这行:


[Objective-C] 纯文本查看 复制代码
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];

我们点进去看看:
[Objective-C] 纯文本查看 复制代码
@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如何解析数据,我们到代理讲完再来补充。
  • 这边还做了一个判断,如果自定义了GCD完成组completionGroup和完成队列的话completionQueue,会在加入这个组和在队列中回调Block。否则默认的是AF的创建的组:


[Objective-C] 纯文本查看 复制代码
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组做任何处理,只是提供这个接口,让我们有需求的自行调用处理。如果有对多个任务完成度的监听,可以自行处理。
    而队列的话,如果你不需要回调主线程,可以自己设置一个回调队列。
  • 回到主线程,发送了任务完成的通知:

[Objective-C] 纯文本查看 复制代码
dispatch_async(dispatch_get_main_queue(), ^{
              [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
          });

  • 这个通知这回AF有用到了,在我们对UIKit的扩展中,用到了这个通知。
AF代理2:
[Objective-C] 纯文本查看 复制代码
- (void)URLSession:(__unused NSURLSession *)session
          dataTask:(__unused NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data
{
    //拼接数据
    [self.mutableData appendData:data];
}

同样被NSUrlSession代理转发到这里,拼接了需要回调的数据。
AF代理3:
[Objective-C] 纯文本查看 复制代码
- (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代理转发到这里,这里有个地方需要注意下:
  • 之前的NSUrlSession代理和这里都移动了文件到下载路径,而NSUrlSession代理的下载路径是所有request公用的下载路径,一旦设置,所有的request都会下载到之前那个路径。
  • 而这个是对应的每个task的,每个task可以设置各自下载路径,还记得AFHttpManager的download方法么



[Objective-C] 纯文本查看 复制代码
[manager downloadTaskWithRequest:resquest progress:nil destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
  return path;
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
}];

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


[Objective-C] 纯文本查看 复制代码
- (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代理成功失败回调,回到了用户的手中。

文 /涂耀辉(简书作者)
原文链接:http://www.jianshu.com/p/f32bd79233da



举报 使用道具
| 回复

共 0 个关于本帖的回复 最后回复于 2017-1-14 20:37

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

本文内容不够精彩,我要自己发布

发布新帖

推荐阅读

    拥有的,不仅是技术!还有...
    联系 Email: support.36ji@qq.com

    • 关注酷站官方微博
      了解最新动态

    • 关注酷站微信公众号
      这里有好玩的讯息

    • 加入酷站交流群
      不断在这里成长

    © 2014-2017 36ji网络科技有限公司 . All rights reserved.
    京ICP备14001609号

    Archiver|    
    Powered by Discuz! X3.2 © 2001-2013 Comsenz Inc.
    快速回复 返回顶部 返回列表