探秘AFNetworking

作者|刘小壮 字数:9029

本文预计阅读时间:29分钟

AFNetworkingiOS最常用的网络框架,虽然系统也有NSURLSession,但是我们一般不会直接用它。AFNetworking经过了三个大版本,现在用的大多数都是3.x的版本。

AFNetworking经历了下面三个阶段的发展:

  • 1.0版本 : 基于NSURLConnection的封装。

  • 2.0版本 : 两套实现,分别基于NSURLConnectionNSURLSession,是转向NSURLSession的过渡版。

  • 3.0版本 : 基于NSURLSession的封装。

文件构成

AFNetworking3.X的构成很简单,主要就四部分,除此之外还有一些基于UIKitCategory,但这些并不是标配。

  • Manager : 负责处理网络请求的两个Manager,主要实现都在AFURLSessionManager中。

  • Reachability : 网络状态监控。

  • Security : 处理网络安全和HTTPS相关的。

  • Serialization : 请求和返回数据的格式化器。

AFURLSessionManager

AFN3.0中,网络请求的manager主要有AFHTTPSessionManagerAFURLSessionManager构成,二者为父子关系。这两个类职责划分很清晰,父类负责处理一些基础的网络请求代码,并且接受NSURLRequest对象,而子类则负责处理和http协议有关的逻辑。

AFN的这套设计很便于扩展,如果以后想增加FTP协议的处理,则基于AFURLSessionManager创建子类即可。子类中只需要进行很少的代码处理,创建一个NSURLRequest对象后调用父类代码,由父类去完成具体的请求操作。

创建sessionManager

AFHTTPSessionManager类的初始化方法中并没有太多实现代码,其内部调用的都是父类AFURLSessionManagerinitWithSessionConfiguration方法,下面是此方法内部的一些关键代码。

在初始化方法中包含一个参数sessionConfiguration,如果没有传入的话默认是使用系统的defaultConfiguration,我们创建是一般都不会自定义configuration,所以大多数都是系统的。

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

随后是NSURLSession的初始化代码,关于NSOperationQueue后面详细进行讲解。NSURLSession的初始化方式有两种,一种是使用系统的共享session,另一种是自己创建sessionAFN选择的是创建自己的session,并且每个请求都会创建一个独立的session

可以通过NSURLSession进行连接复用,这样可以避免很多握手和挥手的过程,提高网络请求速度,苹果允许iOS设备上一个域名可以有四个连接同时存在。但是由于AFN的实现是每个请求都创建一个session,所以就不能进行连接复用。

所以可以通过在外面对AFN进行二次封装,将AFHTTPSessionManager复用为单例对象,通过复用sessionManager的方式,来进行连接的复用。但是这种方案对于不同的requestSerializerresponseSerializer等情况,还是要做特殊兼容,所以最好建立一个sessionManager池,对于同类型的sessionManager直接拿出来复用,否则就创建新的。

// 共享session连接池
[NSURLSession sharedSession];
// 创建新session,则不能使用共享session连接池
[NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];

由于当前AFURLSessionManager对象的所有sessionTask请求任务,都是共享同一个回调代理的,所以AFN为了区分每个sessionTask,通过下面的可变字典,将所有taskDelegatetask.taskIdentifier的进行了一一对应,以便于很容易的对每个请求task进行操作。

self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];

在初始化方法中,可以发现AFN在创建session后,调用了getTasksWithCompletionHandler方法来获取当前所有的task。但是现在刚创建session,理论上来说是不应该有task的。但从AFN的issues中找到了答案issues 3499。

这是因为,在completionHandler回调中,为了防止进入前台时,通过session id恢复的task导致一些崩溃问题,所以这里将之前的task进行遍历,并将回调都置nil

[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];
    }
}];

创建task

AFURLSessionManager中进行task的创建,task的类型总共分为三种,dataTaskuploadTaskdownloadTaskAFN并没有对streamTask进行处理。

AFHTTPSessionManager在创建GETPOST等请求时,本质上都是调用了下面的方法或其类似的方法,方法内部会创建一个task对象,并调用addDelegateForDataTask将后面的处理交给AFURLSessionManagerTaskDelegate来完成。随后会将task返回给调用方,调用方获取到task对象后,也就是子类AFHTTPSessionManager,会调用resume方法开始请求。

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

除了普通请求外,uploaddownload都有类似的处理。

addDelegateForDataTask方法中,会调用sessionManagersetDelegate:forTask:方法,此方法内部将tasktaskDelegate进行了注册。由于AFN可以通过通知让外界监听请求状态,所以在此方法中还监听了taskresumesuspend事件,并在实现代码中将事件广播出去。

- (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] initWithTask:dataTask];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

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

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

如果从AFHTTPSessionManager的创建任务开始,按代码逻辑跟到这里,发现其实AFN3.0的请求代码真的很简单,主要都集中在创建NSMutableURLRequest那里,其他都依赖于NSURLSession,因为确实NSURLSessionAPI封装程度比较好,也很好使用。

AFN3.0的作用就是对NSURLSession的封装性比较好,你不用去写太多重复性的代码,并且可以很容易的通过block得到回调结果。

AFURLSessionManagerTaskDelegate

NSURLSession的回调方法比较多,这里只针对一些关键代码进行讲解,以及梳理整体回调逻辑,不一一列举每个回调方法的作用,详细源码各位可以直接下载AFN代码查看。

AFURLSessionManager中,有一个AFURLSessionManagerTaskDelegate类比较重要,这个类和sessionTask是一一对应的,负责处理sessionTask请求的很多逻辑,NSURLSessionDelegate的回调基本都转发给taskDelegate去处理了。在NSURLSession回调中处理了HTTPS证书验证、下载进度之类的,没有太复杂的处理。

taskDelegate的设计很不错,可以将代理回调任务处理对象化,也可以给AFURLSessionManager类瘦身。比较理想的是直接将代理设置为taskDelegate,但是由于会涉及一些AFURLSessionManager自身的处理逻辑,所以才设计为消息传递的方式。

taskDelegate的功能很简单,主要是NSData数据的处理,NSProgress上传下载进度的处理,以及通知参数的处理。在进行AFN的下载处理时,NSData的数据拼接、事件回调,及文件处理,都是由taskDelegate来完成的。

下面是downloadTask任务完成时的处理代码,其他回调代码就不一一列举了。

- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
    self.downloadFileURL = nil;

    if (self.downloadTaskDidFinishDownloading) {
        self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
        if (self.downloadFileURL) {
            NSError *fileManagerError = nil;

            if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFil
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值