首页 > 代码库 > AFNetWorking VS NSURLSession

AFNetWorking VS NSURLSession

为了写这篇文章,特意写了前一篇NSURLSession,为了更好的理解,可以先看看前一篇。

本文目的在于讲解AFNetWorking比起原生的NSURLSession发生网络请求,都帮我们做了些什么呢,世人都喜欢AF,那AF到底有多漂亮,下面我会对比原生的NSURLSession揭开AF神秘的面纱,让我们一起目睹她的芳容。

我先梳理一下发起一个网络请求的步骤:

1.构建请求

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];

2.创建会话

NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];

NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:queue];

3.创建请求任务

NSURLSessionDataTask *task = [session dataTaskWithRequest:request];

[task resume];

4.请求状态的跟踪(会话状态、数据任务状态、下载状态)

NSURLSessionDelegate  NSURLSessionTaskDelegate  NSURLSessionDataDelegate  NSURLSessionDownloadDelegate

5.请求结果处理

[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]

 

那么AF在这五部里面都帮我们做了些什么呢,让我们一步一步来扔掉她的扇子,拉下她的纱巾:

1.构建请求

AF中的请求创建源码是这样的

NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];

然后它做对mutableRequest了一系列的设置:

a.属性设置:这个AF还真没有做什么,交给我们自己设置

b.请求头填写:

    [mutableRequest setValue:@"" forHTTPHeaderField:@"Accept-Language"];

    [mutableRequest setValue:@"" forHTTPHeaderField:@"User-Agent"];

    [mutableRequest setValue:@"" forHTTPHeaderField:@"Authorization"];

    [mutableRequest setValue:@"" forHTTPHeaderField:@"Content-Type"];

打印了下AF请求的请求头得到:

  Accept-Language = zh-Hans-US;q=1, en-US;q=0.9,

  User-Agent = test2/1.0 (iPhone; iOS 9.3; Scale/3.00) 

用这个方法让我们设置Authorization更方便了

- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username password:(NSString *)password{

    NSData *basicAuthCredentials = [[NSString stringWithFormat:@"%@:%@", username, password] dataUsingEncoding:NSUTF8StringEncoding];

    NSString *base64AuthCredentials = [basicAuthCredentials base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0];

    [self setValue:[NSString stringWithFormat:@"Basic %@", base64AuthCredentials] forHTTPHeaderField:@"Authorization"];

}

c.参数填写:

参数填写还是帮我们做了很多复杂的事情,有下面几个:

(1)url编码

(2)请求头里参数的数据类型,参数转码

  //默认的

  [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];

  [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];

 //json

  [mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];

  [mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]];

 //数据列表

  [mutableRequest setValue:@"application/x-plist" forHTTPHeaderField:@"Content-Type"];

  [mutableRequest setHTTPBody:[NSPropertyListSerialization dataWithPropertyList:parameters format:self.format options:self.writeOptions error:error]];

d.上传文件的body拼接

2.创建会话

AF中创建会话的代码是这样的

- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString

                            parameters:(nullable id)parameters

                              progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress

                               success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success

                               failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString

                             parameters:(nullable id)parameters

                               progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress

                                success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success

                                failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString

                             parameters:(nullable id)parameters

              constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block

                               progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress

                                success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success

                                failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

从这几个方法的观察,就可以知道AF帮我们做了以下几点:

  a.我们可以不用创建NSURL ,可以直接使用NSString;

  b.不用再自己拼接URL的参数,可以直接通过字典快捷的填写;(而且参数中有中文什么的都可以直接使用,AF里面已经帮你做了url编码)

  c.不用自己到代理方法中获取数据传输的进度,可以直接通过设置block,等待回调就行;

  d.请求结果的回调,只不过原生的NSURLSession分类已经做到这一点了;

  e.不用自己在请求体里拼接复杂的格式化的那一堆东西,上传数据可以通过block直接填写;

在我看来实现这五点已经相当厉害了,长期使用NSURLSession的人光看见AF这样的请求方法就能惊呆了,这减去了平时多少的代码量。

对应NSURLSession的部分代码:

  self.sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];

    self.operationQueue = [[NSOperationQueue alloc] init];

    self.operationQueue.maxConcurrentOperationCount = 1;

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

3.创建请求任务

AF中创建任务的代码是这样的

    __block NSURLSessionDataTask *dataTask = nil;

    url_session_manager_create_task_safely(^{

        dataTask = [self.session dataTaskWithRequest:request];

    });

    __block NSURLSessionUploadTask *uploadTask = nil;

    url_session_manager_create_task_safely(^{

        uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];

    });

    __block NSURLSessionUploadTask *uploadTask = nil;

    url_session_manager_create_task_safely(^{

        uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];

    });

    __block NSURLSessionUploadTask *uploadTask = nil;

    url_session_manager_create_task_safely(^{

        uploadTask = [self.session uploadTaskWithStreamedRequest:request];

    });

    __block NSURLSessionDownloadTask *downloadTask = nil;

    url_session_manager_create_task_safely(^{

        downloadTask = [self.session downloadTaskWithRequest:request];

    });

它们都是在一个单列的串行队列中,同步创建的。这个线程的创建代理如下:(有删减)

//AFNetworking中所有的和创建任务相关的事件都放到了一个单例的串行队列中

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;

}

static void url_session_manager_create_task_safely(dispatch_block_t block) {

        dispatch_sync(url_session_manager_creation_queue(), block);

}

4.请求状态的跟踪

AF中协议的代码是这样的

//验证服务端证书链

- (void)URLSession:(NSURLSession *)session

didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge

 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler

{

    // 创建默认的处理方式,PerformDefaultHandling方式将忽略credential这个参数

    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;

    __block NSURLCredential *credential = nil;

    if (self.sessionDidReceiveAuthenticationChallenge) {

        disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);

    } else {    // 如果没有实现自定义的验证过程

         // 判断challenge的authenticationMethod

        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {

             // 使用安全策略来验证

            if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {

                // 如果验证通过,根据serverTrust创建依据

                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];

                if (credential) {// 有的话就返回UseCredential

                    disposition = NSURLSessionAuthChallengeUseCredential;

                } else {

                    disposition = NSURLSessionAuthChallengePerformDefaultHandling;

                }

            } else {

                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;

            }

        } else {

            disposition = NSURLSessionAuthChallengePerformDefaultHandling;

        }

    }

    if (completionHandler) {

        completionHandler(disposition, credential);

    }

}

//下载数据的获取

- (void)URLSession:(__unused NSURLSession *)session

          dataTask:(__unused NSURLSessionDataTask *)dataTask

    didReceiveData:(NSData *)data

{

    [self.mutableData appendData:data];

}

//把下载文件转移到我们之前设定的地址

- (void)URLSession:(NSURLSession *)session

      downloadTask:(NSURLSessionDownloadTask *)downloadTask

didFinishDownloadingToURL:(NSURL *)location

{

    NSError *fileManagerError = nil;

    self.downloadFileURL = nil;

    if (self.downloadTaskDidFinishDownloading) {

        self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);

        if (self.downloadFileURL) {

            [[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError];

        }

    }

}

//数据解析      对请求任务结束的回调block进行线程优化

- (void)URLSession:(__unused NSURLSession *)session

              task:(NSURLSessionTask *)task

didCompleteWithError:(NSError *)error

{

     NSData *data = http://www.mamicode.com/nil;

     if (self.mutableData) {

            data = http://www.mamicode.com/[self.mutableData copy];

        self.mutableData = http://www.mamicode.com/nil;

      }

  dispatch_async(url_session_manager_processing_queue(), ^{  //异步并行,加快数据的处理时间

    //数据解析

 

    responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&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);

                }

            });

  });

}

//上面GCD括号里的参数可能不好理解,两个参数都用了?:来设定,意思是AF的使用者定义了completionGroup就用使用者定义的,没定义就用AF提供的,第二个参数同理。

// 它们的类型dispatch_queue_t completionQueue;    dispatch_group_t completionGroup,下面是这个 url_session_manager_completion_group()的代码:

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;

}

//这个方法创建的队列是一个并行的队列,这加快了数据的处理速度

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;

}

5.请求结果处理

1.AFHTTPResponseSerializer

//这个方法根据在初始化方法中初始化的属性 `acceptableContentTypes` 和 `acceptableStatusCodes` 来判断当前响应是否有效。

- (BOOL)validateResponse:(NSHTTPURLResponse *)response data:(NSData *)data error:(NSError * __autoreleasing *)error

 

AFJSONResponseSerializer : AFHTTPResponseSerializer

- (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error

{

//1. 验证请求的有效性

    if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {

        if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {

            return nil;

        }

    }

//2. 解决一个空格引起的 [bug]

    id responseObject = nil;

    NSError *serializationError = nil;

 

    BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];

    if (data.length > 0 && !isSpace) {

//3. 序列化 JSON

        responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];

    } else {

        return nil;

    }

//4. 移除 JSON 中的 null

    if (self.removesKeysWithNullValues && responseObject) {

        responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);

    }

    if (error) {

        *error = AFErrorWithUnderlyingError(serializationError, *error);

    }

    return responseObject;

}

2.AFXMLParserResponseSerializer 

    responseObject =[[NSXMLParser alloc] initWithData:data];

3.AFXMLDocumentResponseSerializer 

  responseObject = [[NSXMLDocument alloc] initWithData:data options:self.options error:&serializationError];

4.AFPropertyListResponseSerializer 

  responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError];

 

 

 

 

 

 

 

 

 

 

 ----------------------持续更新中  如有错误   恳请指正----------------------------

AFNetWorking VS NSURLSession