美文网首页iOS Developer程序员
AFNetworking源码分析 (1)--AFHTTPSess

AFNetworking源码分析 (1)--AFHTTPSess

作者: Arnold134777 | 来源:发表于2017-06-01 20:40 被阅读652次

AF版本基于3.0,下面将从使用切入开始分析。

1.使用

例子:

AFHTTPSessionManager *session = [AFHTTPSessionManager manager];
session.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/html",@"application/json", @"text/json" ,@"text/javascript", nil];
session.responseSerializer = [AFHTTPResponseSerializer serializer];
[session GET:@"https://www.baidu.com"
  parameters:nil
    progress:nil
     success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
         NSLog(@"请求成功");
     } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
          NSLog(@"请求失败");
     }];

2 代码分析

2.1 [AFHTTPSessionManager manager]

AFHTTPSessionManager is a subclass of AFURLSessionManager with convenience methods for making HTTP requests. When a baseURL is provided, requests made with the GET / POST / et al. convenience methods can be made with relative paths.

我们加入断点查看一下manager的初始化调用栈如下图:

进入到AFURLSessionManager中的manager中分析:

// 1.设置全局的网络行为策略的配置
self.sessionConfiguration = configuration; 

// 2.设置请求的队列,默认最大的并发数为1
self.operationQueue = [[NSOperationQueue alloc] init]; 
self.operationQueue.maxConcurrentOperationCount = 1;

// 3.根据configuration,operationQueue初始化全局的NSURLSession
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];

// 4.设置网络请求响应的数据解析实例
self.responseSerializer = [AFJSONResponseSerializer serializer];

// 5.设置网络请求安全策略实例(后续针对这个做具体说明)
self.securityPolicy = [AFSecurityPolicy defaultPolicy];

// 6.初始化全局的网络状态监听的实例
#if !TARGET_OS_WATCH
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif

// 7.将taskId与其delegate绑定,实现解耦,后续对整个过程做分析
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];

2.2.全局的网络行为策略的配置 NSURLSessionConfiguration

An NSURLSessionConfiguration object defines the behavior and policies to use when uploading and downloading data using an NSURLSession object. When uploading or downloading data, creating a configuration object is always the first step you must take. You use this object to configure the timeout values, caching policies, connection requirements, and other types of information that you intend to use with your NSURLSession object.

简单罗列为一下几点:

  • NSURLSessionConfiguration 可以控制网络请求中的缓存策略,超时设置等。
  • 如果需要更改网络请求的行为策略必须重新在更改NSURLSessionConfiguration后再创建一个新的NSURLSession对象。

2.3 请求的方法GET:parameters:process:success:failure:

2.3.1

具体调用栈如下图:


这里引入了一个新的类:NSURLSessionDataTask,继承了NSURLSessionTask,我们看看官方的说明:

The NSURLSessionTask class is the base class for tasks in a URL session. Tasks are always part of a session; you create a task by calling one of the task creation methods on an NSURLSession object. The method you call determines the type of task.
URL sessions provide three types of tasks: data tasks, upload tasks, and download tasks. These tasks are instances of the NSURLSessionDataTask, NSURLSessionUploadTask, NSURLSessionDownloadTask, NSURLSessionStreamTask subclasses of NSURLSessionTask, respectively.

简单罗列为一下几点:

  • NSURLSessionTask是官方提供的几种网络任务类的基类。
  • 官方提供了三种任务处理的子类:简单数据处理任务类,上传任务类,下载任务类。

介绍了这其中使用到的核心类NSURLSessionTask,我们由底层向上看一下具体的调用流程。

2.3.2 [AFHTTPSessionManager dataTaskWithHTTPMethod:...]

// 1.通过全局配置的requestSerializer 初始化一个请求的实例
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];

// 2.根据请求的实例再初始化一个task的实例
 __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);
        }
    }
}];

2.3.2 task与delegate的绑定[AFURLSessionManager addDelegateForDataTask:...]

这里是初始化一个请求task的基本步骤:

 // 1.先根据请求信息初始化一个task的实例
url_session_manager_create_task_safely(^{
    dataTask = [self.session dataTaskWithRequest:request];
});

[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
{
   
    // 2.初始化一个任务的代理
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
    delegate.manager = self;
    
    // 此处赋值了请求完成的回调,后续会用到
    delegate.completionHandler = completionHandler;
    
    // 2.利用全局的字典存储绑定信息,key为taskId,value为代理的实例
    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:dataTask];

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

- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    NSParameterAssert(task);
    NSParameterAssert(delegate);

    [self.lock lock];
   // 1.绑定taskId与代理self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
   // 2.使用kvo对一些方法监听,返回上传或者下载的进度
    [delegate setupProgressForTask:task];
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

2.3.3 URLSession:task:didCompleteWithError:

当请求收到了响应后,会触发该回调,我们分析一下他具体的处理。

// AFURLSessionManager.m
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
    // 1.根据task获取绑定的代理实例
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];

    // delegate may be nil when completing a task in the background
    if (delegate) {
        // 2.统一处理
        [delegate URLSession:session task:task didCompleteWithError:error];

        [self removeDelegateForTask:task];
    }

    if (self.taskDidComplete) {
        self.taskDidComplete(session, task, error);
    }
}

// AFURLSessionManagerTaskDelegate.m
- (void)URLSession:(__unused NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
    
    if (error) {
        userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
        // 1.iOS网络框架返回的错误信息处理
        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 {
        dispatch_async(url_session_manager_processing_queue(), ^{         
           // 2.请求成功后需要用响应的数据解析类实例处理返回数据,同样如果出现错误则回调上层
            NSError *serializationError = nil;
            responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];

            if (self.downloadFileURL) {
                responseObject = self.downloadFileURL;
            }

            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(), ^{
            
                // 3.绑定task与处理的数据代理时,传入代理的完成的回调
                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
}

3 调试中遇到的问题的解决

1.请求收到响应:response Code=-1016 "Request failed: unacceptable content-type: text/html"

代码段:

AFHTTPSessionManager *session = [AFHTTPSessionManager manager];
[session GET:@"https://www.baidu.com"
  parameters:nil
    progress:nil
     success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
         NSLog(@"请求成功");
     } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
          NSLog(@"请求失败");
     }];

解决:响应加入对text/html格式的支持

session.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/html",@"application/json", @"text/json" ,@"text/javascript", nil];

2.Code=3840 "JSON text did not start with array or object and option to allow fragments not set."

解决:AF默认设置的响应的解析类型为json,因此需要改变解析类型。
解决:

session.responseSerializer = [AFHTTPResponseSerializer serializer];

3.AFNetworking 与 RunLoop 之间的关系,3.0为什么不需要加入如下的段?

我们看看NSURLConnection 的官方文档描述:

These delegate methods are called on the thread that initiated the asynchronous load operation.

NSURLConnection的delegate方法需要在connection发起的线程的runloop中调用。因此,当发起connection的线程exit了,delegate自然不会被调用,请求也就回不来了。因此AF 2.X 加入了NSThread + runLoop去解决这个问题:

+ (void)networkRequestThreadEntryPoint:(id)__unused object {
    @autoreleasepool {
        [[NSThread currentThread] setName:@"AFNetworking"];
        // 创建一个runloop,添加对input source的的监听
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
        [runLoop run];
    }
}

+ (NSThread *)networkRequestThread {
    static NSThread *_networkRequestThread = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
        [_networkRequestThread start];
    });

    return _networkRequestThread;
}

而AF 3.0为什么不需要做类似的处理呢?我们看看官方的说明:

Thread Safety

The URL session API itself is fully thread-safe. You can freely create sessions and tasks in any thread context, and when your delegate methods call the provided completion handlers, the work is automatically scheduled on the correct delegate queue

相关文章

网友评论

    本文标题:AFNetworking源码分析 (1)--AFHTTPSess

    本文链接:https://www.haomeiwen.com/subject/okzefxtx.html