请选择 进入手机版 | 继续访问电脑版
0 1237

AFNetworking拆解分析(三)

admin 于 2017-1-11 22:29 发表 [复制链接]
afnetworking.png
我们再回到AFHTTPSessionManager类中来,回到这个方法:
[Objective-C] 纯文本查看 复制代码
- (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;
}
绕了一圈我们又回来了。。
  • 我们继续往下看:当解析错误,我们直接调用传进来的fauler的Block失败返回了,这里有一个self.completionQueue,这个是我们自定义的,这个是一个GCD的Queue如果设置了那么从这个Queue中回调结果,否则从主队列回调。
  • 实际上这个Queue还是挺有用的,之前还用到过。我们公司有自己的一套数据加解密的解析模式,所以我们回调回来的数据并不想是主线程,我们可以设置这个Queue,在分线程进行解析数据,然后自己再调回到主线程去刷新UI。

言归正传,我们接着调用了父类的生成task的方法,并且执行了一个成功和失败的回调,我们接着去父类AFURLSessionManger里看(总算到我们的核心类了..):
[Objective-C] 纯文本查看 复制代码
- (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;
}
  • 我们注意到这个方法非常简单,就调用了一个url_session_manager_create_task_safely()函数,传了一个Block进去,Block里就是iOS原生生成dataTask的方法。此外,还调用了一个addDelegateForDataTask的方法。
  • 我们到这先到这个函数里去看看:

[Objective-C] 纯文本查看 复制代码
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

    //理解下,第一为什么用sync,因为是想要主线程等在这,等执行完,在返回,因为必须执行完dataTask才有数据,传值才有意义。
    //第二,为什么要用串行队列,因为这块是为了防止ios8以下内部的dataTaskWithRequest是并发创建的,
    //这样会导致taskIdentifiers这个属性值不唯一,因为后续要用taskIdentifiers来作为Key对应delegate。
      dispatch_sync(url_session_manager_creation_queue(), block);
  } else {
      block();
  }
}
static dispatch_queue_t url_session_manager_creation_queue() {
  static dispatch_queue_t af_url_session_manager_creation_queue;
  static dispatch_once_t onceToken;
  //保证了即使是在多线程的环境下,也不会创建其他队列
  dispatch_once(&onceToken, ^{
      af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL);
  });

  return af_url_session_manager_creation_queue;
}

    • 方法非常简单,关键是理解这么做的目的:为什么我们不直接去调用
      dataTask = [self.session dataTaskWithRequest:request];
      非要绕这么一圈,我们点进去bug日志里看看,原来这是为了适配iOS8的以下,创建session的时候,偶发的情况会出现session的属性taskIdentifier这个值不唯一,而这个taskIdentifier是我们后面来映射delegate的key,所以它必须是唯一的。
    • 具体原因应该是NSURLSession内部去生成task的时候是用多线程并发去执行的。想通了这一点,我们就很好解决了,我们只需要在iOS8以下同步串行的去生成task就可以防止这一问题发生(如果还是不理解同步串行的原因,可以看看注释)。
    • 题外话:很多同学都会抱怨为什么sync我从来用不到,看,有用到的地方了吧,很多东西不是没用,而只是你想不到怎么用。

我们接着看到:
[Objective-C] 纯文本查看 复制代码
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
调用到:
[Objective-C] 纯文本查看 复制代码
- (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;
}
总结一下:
1)这个方法,生成了一个AFURLSessionManagerTaskDelegate,这个其实就是AF的自定义代理。我们请求传来的参数,都赋值给这个AF的代理了。
2)delegate.manager = self;代理把AFURLSessionManager这个类作为属性了,我们可以看到:
[Objective-C] 纯文本查看 复制代码
@property (nonatomic, weak) AFURLSessionManager *manager;
  • 这个属性是弱引用的,所以不会存在循环引用的问题。
    3)我们调用了[self setDelegate:delegate forTask:dataTask];
我们进去看看这个方法做了什么:
[Objective-C] 纯文本查看 复制代码
- (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];
}
  • 这个方法主要就是把AF代理和task建立映射,存在了一个我们事先声明好的字典里。
  • 而要加锁的原因是因为本身我们这个字典属性是mutable的,是线程不安全的。而我们对这些方法的调用,确实是会在复杂的多线程环境中,后面会仔细提到线程问题。
  • 还有个[delegate setupProgressForTask:task];我们到方法里去看看:
[Objective-C] 纯文本查看 复制代码
- (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];
}


这个方法也非常简单,主要做了以下几件事:
1)设置 downloadProgressuploadProgress的一些属性,并且把两者和task的任务状态绑定在了一起。注意这两者都是NSProgress的实例对象,(这里可能又一群小伙伴楞在这了,这是个什么...)简单来说,这就是iOS7引进的一个用来管理进度的类,可以开始,暂停,取消,完整的对应了task的各种状态,当progress进行各种操作的时候,task也会引发对应操作。
2)给task和progress的各个属及添加KVO监听,至于监听了干什么用,我们接着往下看:
[Objective-C] 纯文本查看 复制代码
- (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触发KVO,则给progress进度赋值,应为赋值了,所以会触发progress的KVO,也会调用到这里,然后去执行我们传进来的downloadProgressBlock和uploadProgressBlock。主要的作用就是为了让进度实时的传递。
    • 主要是观摩一下大神的写代码的结构,这个解耦的编程思想,不愧是大神...
    • 还有一点需要注意:我们之前的setProgress和这个KVO监听,都是在我们AF自定义的delegate内的,是有一个task就会有一个delegate的。所以说我们是每个task都会去监听这些属性,分别在各自的AF代理内。看到这,可能有些小伙伴会有点乱,没关系。等整个讲完之后我们还会详细的去讲捋一捋manager、task、还有AF自定义代理三者之前的对应关系。


到这里我们整个对task的处理就完成了。
文 /涂耀辉(简书作者)
原文链接:http://www.jianshu.com/p/856f0e26279d


举报 使用道具
| 回复

共 0 个关于本帖的回复 最后回复于 2017-1-11 22:29

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

本版积分规则

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

发布新帖

推荐阅读

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

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

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

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

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

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