[iOS开发]AFNetworking源码学习

Posted Billy Miracle

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[iOS开发]AFNetworking源码学习相关的知识,希望对你有一定的参考价值。

目录

简介

AFNetWorking是使用Objective-C开发ios程序主流的网络请求开源库。
小tip
前面的AFURLSessionManger、AFURLSessionManagerTaskDelegate、_AFURLSessionTaskSwizzling、AFHTTPSessionManager、AFSecurityPolicy、AFNetworkReachabilityManager等可作为第一部分学习,后面的UIKit+AFNetworking可作为第二部分单独学习。

开始解读

参考大佬文章
参考大佬文章
参考大佬文章
在使用时,我们要先创建一个AFHTTPSessionManager,调用AFHTTPSessionManagermanager方发来进行初始化,思考一下manager的设计模式是什么呢? 肯定有人会说是单例模式;其实manager所用到的设计模式是工厂设计模式

+ (instancetype)manager 
    return [[[self class] alloc] initWithBaseURL:nil];


- (instancetype)init //未用到
    return [self initWithBaseURL:nil];


- (instancetype)initWithBaseURL:(NSURL *)url 
    return [self initWithBaseURL:url sessionConfiguration:nil];


- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration //未用到
    return [self initWithBaseURL:nil sessionConfiguration:configuration];


- (instancetype)initWithBaseURL:(NSURL *)url
           sessionConfiguration:(NSURLSessionConfiguration *)configuration

    // 最终都会调用父类的初始化方法来生成对应的sessionManager
    self = [super initWithSessionConfiguration:configuration];
    if (!self) 
        return nil;
    

    // Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
    //为了确保NSURL +URLWithstring:relativeToURL:可以正确执行,在baseurlpath的最后添加‘/’
    //url有值且没有‘/’,那么在url的未尾添加‘/’,
	//如果不带‘/’会有一个默认的重定向 浪费带宽
	//有没有反斜杠的意义在于该url是指向一个文件还是一个目录,说明url地址是精准表达指向的,有无'/'代表的是两个不同的地址
	//没有‘/’指向的是网站目录下的一个文件
	//有‘/’指向的是网站目录下的一个目录
    if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) 
        url = [url URLByAppendingPathComponent:@""];
    
    // 初始化URL
    self.baseURL = url;

    self.requestSerializer = [AFHTTPRequestSerializer serializer];
    self.responseSerializer = [AFJSONResponseSerializer serializer];

    return self;

由此可见,在AFHTTPSessionManager中的初始化方法最终都会调用其父类的initWitchSessionConfiguration初始化方法,返回一个sessionManager;那么,需要去看一下父类也就是AFURLSessionManager的初始化都做了什么:

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration 
    self = [super init];
    if (!self) 
        return nil;
    
    // 如果会话配置为nil,对应初始化一个
    if (!configuration) 
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    

    self.sessionConfiguration = configuration;
    // 初始化操作队列,并设置为串行队列 设置最大井发操作数为1
    // 目的是想让并发的请求进行串行的回调
    self.operationQueue = [[NSOperationQueue alloc] init];
    // 1.⚠️
    self.operationQueue.maxConcurrentOperationCount = 1;

    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
    // AFJSONResponseSerializer用来序列化HTTP响应,默认json解析
    self.responseSerializer = [AFJSONResponseSerializer serializer];
    // 初始化SSI所需要的 AFSecurityPolicy 用来保证请求的安全性
    self.securityPolicy = [AFSecurityPolicy defaultPolicy];

#if !TARGET_OS_WATCH
    // AFNetworkReachabilityManager用来查看网络连接情况
    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
	// 2.⚠️
    // 初始化可变任务字典 task 的id 作为 key,代理对象作为 value。
    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
    // 初始化锁& 命名
    self.lock = [[NSLock alloc] init];
    self.lock.name = AFURLSessionManagerLockName;
    // 3.⚠️
    // 获取所有的task,设置一遍 delegate 用来异步的获取当前session的所有未完成的task, session和task的关系是一个session里边会有多 个task
    // 初始化的时候dataTasks-一般为空,这里这么做的主要目的是为了防止从后台回来,重新初始化整个session,
    // 一些之前的后台请求任务,导致程序崩溃
    [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];
        
    ];

    return self;

它做了这些事:

  • 初始化当前的会话配置、操作队列、锁、AFNetworkReachabilityManager、AFSecurityPolicy,请求序列化以及用来存储任务的可变任务字典等属性
  • 获取当前session中所有未完成的task,给它们设置一遍代理

这个方法里有三点需要注意:

  1. 队列的最大并发操作数设置为1,这里的并发操作数值的是回调代理的线程并发数
  2. self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];是用来将每一个请求任务和自定义的AFURLSessionManagerTaskDelegate来建立映射的;(需要深入研究,代理和这里的关系,以及利用KVO的思想实现的相关)
  3. 在初始化的时候获取当前session中的所有task,为它们重新设置一遍代理;一般来说初始化的session中的task应该是为空的,这里这么做的主要目的是为了防止从后台回来的时候初始化session,对于一些后台之前的请求任务没有重设代理导致崩溃的问题;这里不同的任务调用不同的addDelegateForXXX方法来设置代理

对于使用串行队列(并发操作数设置为1),解释是:
An operation queue for scheduling the delegate calls and completion handlers. The queue should be a serial queue, in order to ensure the correct ordering of callbacks. If nil, the session creates a serial operation queue for performing all delegate method calls and completion handler calls.
用于计划委托调用和完成处理程序的操作队列。该队列应为串行队列,以确保回调的正确顺序。如果为 nil,会话将创建一个串行操作队列,用于执行所有委托方法调用和完成处理程序调用。

下面看一下这几个addDelegateForXXX方法:

//为data任务添加代理
- (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

    // 生成局部变量delegate,弱持有AFURLSessionManager对象
    // 设置其manager以及completionHandler
    // 由于这里是弱持有AFURLSessionManager对象,所以不会导致循环引用
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;
    // 设置回话任务的任务描述
    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    // 调用setDelegate存储datatask
    [self setDelegate:delegate forTask:dataTask];

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

//为upload任务添加代理
- (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask
                        progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
               completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler

    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:uploadTask];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

    uploadTask.taskDescription = self.taskDescriptionForSessionTasks;

    [self setDelegate:delegate forTask:uploadTask];

    delegate.uploadProgressBlock = uploadProgressBlock;

//为download任务添加代理
- (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] initWithTask:downloadTask];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

    if (destination) 
		// ⚠️
        delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) 
            return destination(location, task.response);
        ;
    

    downloadTask.taskDescription = self.taskDescriptionForSessionTasks;

    [self setDelegate:delegate forTask:downloadTask];

    delegate.downloadProgressBlock = downloadProgressBlock;

这三个方法都是为不同的任务设置代理,最终都会调用setDelegate设置代理并存储datatask任务:

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

    NSParameterAssert(task);
    NSParameterAssert(delegate);

    [self.lock lock];
    // 以key-value形式存储task到mutableTaskDelegatesKeyedByTaskIdentifier中
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    // 添加监听
    [self addNotificationObserverForTask:task];
    [self.lock unlock];

这个方法主要是把代理和task建立映射关系,并且存储到字典当中,同时为当前task添加监听。在添加监听的方法中,taskDidResumetaskDidSuspend为接收到通知后的处理方法,用来恢复任务和暂停任务。这里,对于初始化的时候获取当前session中的所有task,已经为它们重新设置一遍代理。回到initWitchSessionConfiguration方法中返回当前对象,向上返回,生成AFHTTPSessionManager *sessionManger对象。

AFHTTPSessionManager对象有许多的接口,我们常用的很多接口,比如:

- (NSURLSessionDataTask *)POST:(NSString *)URLString
                    parameters:(nullable id)parameters
     constructingBodyWithBlock:(nullable void (^)(id<AFMultipartFormData> _Nonnull))block
                       success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
                       failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure;

- (NSURLSessionDataTask *)GET:(NSString *)URLString
                   parameters:(id)parameters
                     progress:(void (^)(NSProgress * _Nonnull))downloadProgress
                      success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
                      failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure;

.
.
.

很多我们常使用的GET、POST接口(有一些不是),底层都调用了:

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

方法,下面看看实现:

- (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相关属性&参数,(requestSerializer:请求序列化器)
    // 1.调用请求序列化类中的requestWithMethod方法进行序列化处理
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    //序列化失败的回调处理
    if (serializationError) 
        if (failure) 
            //completionQueue 如果设置了这个GCD的queue,那么从这个completionQueue中回调结果就好
            // 否则就从主队列回调
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^
                failure(nil, serializationError);
            );
        

        return nil;
    
    // NSURLSessionManager创建一个datatask
    // 2.调用dataTaskWithRequest来生成一个datatask任务
    __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;

先看看调用请求序列化类中的requestWithMethod方法进行序列化处理:
通过requestSerializer来调用requestWithMethod对请求参数进行序列化,最终生成一个最终请求网络需要的request实例;这里想一想:为什么要进行序列化?下面,先看看requestWithMethod方法内部做了些什么吧。

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(id)parameters
                                     error:(NSError *__autoreleasing *)error

    // 判断参数是否存在 url 转化
    NSParameterAssert(method);
    NSParameterAssert(URLString);

    NSURL *url = [NSURL URLWithString:URLString];

    NSParameterAssert(url);
    // 利用url创建NSMutableURLRequest井设置http请求方法
    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
    mutableRequest.HTTPMethod = method;
    // 循环遍历mutableRequest
    // 在self.mutableObservedChangedKeyPaths根据keypath取出对应的value值,
    // 存到创建的mutableRequest中
    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) 
        if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) 
        	//通过KVC赋值
            [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
        
    
    // 对mutableRequest参数做编码井且重新赋值给mutableRequest
    //  调用requestBySerializingRequest方法将传入的parameters参数进行编码,井添加到request中
    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];

	return mutableRequest;

requestWitchMethod方法中,做了三件事情:

  • 创建mutableRequest并设置其请求方法
  • 把当前类设置的一些属性设置给mutableRequest
  • 把需要传递的参数进行编码并且设置到mutableRequest当中

下面看一下编码的代理方法:

#pragma mark - AFURLRequestSerialization
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(id)parameters
                                        error:(NSError *__autoreleasing *)error

    NSParameterAssert(request);

    NSMutableURLRequest *mutableRequest = [request mutableCopy];
    // 从自己持有的HTTPRequestHeaders中遍历,如果有值,就设置给mutableRequest的head
    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) 
        if (![request valueForHTTPHeaderField:field]) 
            [mutableRequest setValue:value forHTTPHeaderField:field];
        
    ];
    //把网络请求的参数转换为NSString类型
    NSString *query = nil;
    if (parameters) 
        if (self.queryStringSerialization) 
            NSError *serializationError;
            query = self.queryStringSerialization(request, parameters, &serializationError);

            if (serializationErroriOS 网络资源汇总之网络

iOS 网络资源汇总之网络

iOS开发值得学习的Demo

对比iOS网络组件:AFNetworking 和 ASIHTTPRequest

对比iOS网络组件:AFNetworking 和 ASIHTTPRequest

学习总结AFNetworking源码阅读