美文网首页iOS
AFNetworking主线梳理(一)

AFNetworking主线梳理(一)

作者: ChinaChong | 来源:发表于2019-11-18 14:58 被阅读0次

    全篇使用的案例Demo

    最简单的AFN使用例子,本文使用的是AFNetworking v3.2.1

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

    其中URL@"https://www.baidu.com"可以替换成自己项目中的URL,否则如果使用百度的URL需要在AFHTTPResponseSerializer中设置ContentType,增加一个类型:text/html

    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    
    AFHTTPResponseSerializer *responseSerializer = [AFHTTPResponseSerializer serializer];
    NSMutableSet *setM = [NSMutableSet setWithSet:manager.responseSerializer.acceptableContentTypes];
    [setM addObject:@"text/html"];
    [responseSerializer setAcceptableContentTypes:setM];
    [manager setResponseSerializer:responseSerializer];
    
    [manager POST:@"https://www.baidu.com" parameters:@{} progress:^(NSProgress * _Nonnull uploadProgress) {
    
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSLog(@"成功");
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSLog(@"失败 %@", error);
    }];
    

    本篇整理AFN的初始化,也就是这句代码 AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

    AFHTTPSessionManager 部分

    案例Demo的入口方法就是 AFHTTPSessionManager + manager方法,从 + manager方法 开始。

    manager方法

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

    类方法 + manager没什么好说的,直接调用了自己的 initWithBaseURL: 方法。

    initWithBaseURL:方法

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

    也没什么好说的,直接调用了自己的 initWithBaseURL:sessionConfiguration: 方法。

    initWithBaseURL:sessionConfiguration: 方法

    - (instancetype)initWithBaseURL:(NSURL *)url
               sessionConfiguration:(NSURLSessionConfiguration *)configuration
    {
        self = [super initWithSessionConfiguration:configuration];
        if (!self) {
            return nil;
        }
    
        // Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
        if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
            url = [url URLByAppendingPathComponent:@""];
        }
    
        self.baseURL = url;
    
        self.requestSerializer = [AFHTTPRequestSerializer serializer];
        self.responseSerializer = [AFJSONResponseSerializer serializer];
    
        return self;
    }
    


    概述
    initWithBaseURL:sessionConfiguration: 方法的核心是 [super initWithSessionConfiguration:configuration]。另外,self.requestSerializer = [AFHTTPRequestSerializer serializer];self.responseSerializer = [AFJSONResponseSerializer serializer]; 也都是重点。

    整体逻辑

    1. 调用父类 AFURLSessionManager 的initWithSessionConfiguration:方法,创建一个 AFHTTPSessionManager 实例对象,用 self 接收。
    2. 处理入参 url 的格式(主要是斜杠'/'),以确保NSURL +URLWithString:relativeToURL: 可以正常工作。
    3. 将入参 url 保存到 AFHTTPSessionManager 的 baseURL 属性中。
    4. 初始化 AFHTTPRequestSerializer 并将实例对象保存到 requestSerializer 属性中。
    5. 初始化 AFJSONResponseSerializer 并将实例对象保存到 responseSerializer 属性中。

    小结
    AFHTTPSessionManager 是最上层的类,没有什么可看的,都是在调用别的类的方法为自己服务。整体逻辑1、4、5中涉及的三个重点方法解析地图:

    1. initWithSessionConfiguration:方法,转到 AFURLSessionManager 部分(往下翻)。
    2. [AFHTTPRequestSerializer serializer],转到 AFHTTPRequestSerializer 部分(往下翻完往下翻)。
    3. [AFJSONResponseSerializer serializer],转到 AFJSONResponseSerializer 部分(使劲往下翻)。

    AFURLSessionManager 部分(高能预警!!!)

    这里填第一个坑:
    AFHTTPSessionManager 部分 => initWithBaseURL:sessionConfiguration: 方法 => 小结 => 第1条转到 AFURLSessionManager 部分

    最终目的是创建一个 AFHTTPSessionManager 实例对象并返回给子类。AFURLSessionManager 部分就从initWithSessionConfiguration:方法开始。

    initWithSessionConfiguration: 方法

    - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
        self = [super init];
        if (!self) {
            return nil;
        }
    
        if (!configuration) {
            configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        }
    
        self.sessionConfiguration = configuration;
    
        self.operationQueue = [[NSOperationQueue alloc] init];
        self.operationQueue.maxConcurrentOperationCount = 1;
    
        self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
    
        self.responseSerializer = [AFJSONResponseSerializer serializer];
    
        self.securityPolicy = [AFSecurityPolicy defaultPolicy];
    
    #if !TARGET_OS_WATCH
        self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
    #endif
    
        self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
    
        self.lock = [[NSLock alloc] init];
        self.lock.name = AFURLSessionManagerLockName;
    
        [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;
    }
    


    整体逻辑

    1. 入参configuration如果为空,调用[NSURLSessionConfiguration defaultSessionConfiguration] 创建 NSURLSessionConfiguration 实例对象,并保存到自己的 sessionConfiguration 属性中。

    2. 创建NSOperationQueue,设置最大并发数为 1,保存到 operationQueue 属性中。

    3. 使用参数 configuration 和 operationQueue 以及代理 self,创建 NSURLSession,保存到 session 属性中。
      此处呼应AFNetworking主线梳理(二)中 AFURLSessionManager 部分的分解说明属性 self.session 解析。

    4. 调用 [AFJSONResponseSerializer serializer] 创建 AFJSONResponseSerializer 实例对象,保存到属性responseSerializer。

    5. 调用 [AFSecurityPolicy defaultPolicy] 创建 AFSecurityPolicy 实例对象,保存到属性securityPolicy。

      • AFSecurityPolicy 本文不展开解析。
    6. 调用 [AFNetworkReachabilityManager sharedManager] 创建 AFNetworkReachabilityManager 实例对象,保存到属性reachabilityManager。

      • AFNetworkReachabilityManager 本文也不展开解析。
    7. 初始化可变字典属性 mutableTaskDelegatesKeyedByTaskIdentifier 。

    8. 创建NSLock,保存到属性lock,并设置name。

    9. 使用属性 session 调用 getTasksWithCompletionHandler: 方法。

    10. getTasksWithCompletionHandler: 方法的block回调中调用 addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler: 方法。

    分解说明

    1. self.session 是用来获取 dataTask 的。
      呼应AFNetworking主线梳理(二)AFURLSessionManager 部分。
    2. [AFJSONResponseSerializer serializer],详情请转到 AFJSONResponseSerializer 部分(使劲往下翻)。
    3. 属性 mutableTaskDelegatesKeyedByTaskIdentifier 是一个可变字典,以 taskIdentifier 为 key 存取 task。
    4. getTasksWithCompletionHandler: 方法用来获取当前尚未完成的各种task。
    5. getTasksWithCompletionHandler: 方法的block回调中有三个数组参数dataTasks、uploadTasks、downloadTasks,for in 逐个添加代理。注意:此block回调在子线程进行。
    6. addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler: 方法是一个重点,详情请继续往下看。

    重点!!!

    addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler: 方法

    addDelegateFor xxx Task方法一共有三个,服务的task类型不同,但是原理和实现是一致的。所以就以dataTask为例。

    1. addDelegateForDataTask
    2. addDelegateForUploadTask
    3. addDelegateForDownloadTask
    - (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;
    }
    


    整体逻辑

    1. 使用入参 dataTask 调用 initWithTask: 方法创建 AFURLSessionManagerTaskDelegate 实例对象,用临时变量 AFURLSessionManagerTaskDelegate *delegate 保存。具体请转到 AFURLSessionManagerTaskDelegate 部分。(往下翻)

    2. delegate保存各种配置。

    3. 调用 setDelegate:forTask: 函数

      • 以 taskIdentifier 为 key ,AFURLSessionManagerTaskDelegate 实例对象为 value,保存到属性 mutableTaskDelegatesKeyedByTaskIdentifier 中。
      - (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
              forTask:(NSURLSessionTask *)task
      {
          NSParameterAssert(task);
          NSParameterAssert(delegate);
      
          [self.lock lock];
          self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
          [self addNotificationObserverForTask:task];
          [self.lock unlock];
      }
      
      • addNotificationObserverForTask:方法在默认通知中心注册两个通知,暂停和开始。
        • self 是 AFURLSessionManager,但是作为 AFHTTPSessionManager 的父类被调用,此处 self 实际上是 AFHTTPSessionManager。
        • 通知中心的 name 分别是:AFNSURLSessionTaskDidResumeNotification 和 AFNSURLSessionTaskDidSuspendNotification
        • 接收通知的方法分别是:taskDidResume:taskDidSuspend:
        • 通知中心传递的参数 object 是入参 dataTask。
      - (void)addNotificationObserverForTask:(NSURLSessionTask *)task {
          [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:task];
          [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:task];
      }
      
    4. delegate保存传进来的 uploadProgressBlock 和 downloadProgressBlock。

    问:这两个通知,都有方法来接收,那是在哪发送(post)的通知呢?

    答:在 _AFURLSessionTaskSwizzling 这个私有类的 af_resume 方法和 af_suspend 方法中触发了通知的post。具体请转到 _AFURLSessionTaskSwizzling 部分。(往下翻)

    1. taskDidResume:方法 和 taskDidSuspend:方法

    提示:下面源码中[task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks],这句代码中 task 的 taskDescription 属性是在 addDelegateForDataTask 方法中设置的。稍微往上翻一点就找到了。

    - (NSString *)taskDescriptionForSessionTasks {
        return [NSString stringWithFormat:@"%p", self];
    }
    
    - (void)taskDidResume:(NSNotification *)notification {
        NSURLSessionTask *task = notification.object;
        if ([task respondsToSelector:@selector(taskDescription)]) {
            if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task];
                });
            }
        }
    }
    
    - (void)taskDidSuspend:(NSNotification *)notification {
        NSURLSessionTask *task = notification.object;
        if ([task respondsToSelector:@selector(taskDescription)]) {
            if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidSuspendNotification object:task];
                });
            }
        }
    }
    

    _AFURLSessionTaskSwizzling 部分

    核心就在 + load 方法里:af_resumeaf_suspend 方法是AFN自定义的方法,通过methodSwizzing交换了系统方法 resumesuspend。也就是说调用系统的 resumesuspend 就等于调用 af_resumeaf_suspend 方法。

    static inline void af_swizzleSelector(Class theClass, SEL originalSelector, SEL swizzledSelector) {
        Method originalMethod = class_getInstanceMethod(theClass, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(theClass, swizzledSelector);
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
    
    static inline BOOL af_addMethod(Class theClass, SEL selector, Method method) {
        return class_addMethod(theClass, selector,  method_getImplementation(method),  method_getTypeEncoding(method));
    }
    
    @interface _AFURLSessionTaskSwizzling : NSObject
    @end
    
    @implementation _AFURLSessionTaskSwizzling
    
    + (void)load {
        if (NSClassFromString(@"NSURLSessionTask")) {
            NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
            NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Wnonnull"
            NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
    #pragma clang diagnostic pop
            IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
            Class currentClass = [localDataTask class];
            
            while (class_getInstanceMethod(currentClass, @selector(resume))) {
                Class superClass = [currentClass superclass];
                IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
                IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
                if (classResumeIMP != superclassResumeIMP &&
                    originalAFResumeIMP != classResumeIMP) {
                    [self swizzleResumeAndSuspendMethodForClass:currentClass];
                }
                currentClass = [currentClass superclass];
            }
            
            [localDataTask cancel];
            [session finishTasksAndInvalidate];
        }
    }
    
    + (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass {
        Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume));
        Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend));
    
        if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) {
            af_swizzleSelector(theClass, @selector(resume), @selector(af_resume));
        }
    
        if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) {
            af_swizzleSelector(theClass, @selector(suspend), @selector(af_suspend));
        }
    }
    
    - (NSURLSessionTaskState)state {
        NSAssert(NO, @"State method should never be called in the actual dummy class");
        return NSURLSessionTaskStateCanceling;
    }
    
    - (void)af_resume {
        NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
        NSURLSessionTaskState state = [self state];
        [self af_resume];
        
        if (state != NSURLSessionTaskStateRunning) {
            [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
        }
    }
    
    - (void)af_suspend {
        NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
        NSURLSessionTaskState state = [self state];
        [self af_suspend];
        
        if (state != NSURLSessionTaskStateSuspended) {
            [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];
        }
    }
    @end
    


    分解说明

    1. af_resumeaf_suspend 方法内部都有这么一句代码:NSURLSessionTaskState state = [self state]; ,再看一眼 _AFURLSessionTaskSwizzling 类中对 state 方法的实现,直接断言了 NO 😅。

      乍一看这个 state 方法根本不能调用,因为内部断言 NO 就会直接崩掉。所以乍一看不行,还得仔细看一看。在 + load 通过methodSwizzing交换了 NSURLSessionDataTask 和 AFN 的开始、暂停方法后,现在 af_resumeaf_suspend 方法内部的 self 实际上不是 _AFURLSessionTaskSwizzling,而是NSURLSessionDataTask。

      因此,调用的 state 方法不是AFN的,而是 NSURLSessionDataTask 的 state 方法。

    2. af_resume 方法内部递归调用了 af_resume 方法(af_suspend 同理)不会有问题吗?
      答:一般来讲 MethodSwizzing 的常规玩法就是交换后自己调用自己,烂大街的那部分就不说了。但是,前面刚说完 af_resumeaf_suspend 方法内部的 self 实际上不是 _AFURLSessionTaskSwizzling,而是NSURLSessionDataTask。NSURLSessionDataTask是系统的类,并没有 af_resumeaf_suspend 方法,那么这里是不是就有问题了呢?

      答:+ load 方法内部做的 MethodSwizzing 方法实现的交换调用的是AFN自定义的 swizzleResumeAndSuspendMethodForClass: 函数。函数的内部实现可以看到,其入参 theClass 就是 + load 里面的 currentClass。而 + load 里面的 currentClass 实际上就是 NSURLSessionDataTask 类。swizzleResumeAndSuspendMethodForClass: 函数把 af_resumeaf_suspend 方法添加到了 NSURLSessionDataTask 类中。这也就解释了为什么 NSURLSessionDataTask 类可以调用 af_resumeaf_suspend 方法。

    3. 如果 NSURLSessionDataTask 不是当前状态,state != NSURLSessionTaskStateRunning(af_suspend 同理)就强制发送通知。
      这里注意:af_resume(af_suspend 同理)方法内部获取的 state 是在调用系统的 resume 之前,一般情况下这里的 state 就是没有开始(开始就是 resume)。所以 state 不是 NSURLSessionTaskStateRunning 就意味着之前任务(task)是空闲状态,是我们调用了系统的 resume 才开启了任务(task),所以在这里发送任务开始的通知。

      这里回答了 AFURLSessionManager 部分最后的那个问题:“这两个通知,都有方法来接收,那是在哪发送(post)的通知呢?”。

    4. af_swizzleSelector()函数和af_addMethod()函数是 MethodSwizzing 的常规玩法,不再赘述。

    有关 _AFURLSessionTaskSwizzling 的一些思考

    1. 为什么需要(会有)_AFURLSessionTaskSwizzling这个私有类?
      简答:解决历史版本的遗留问题。

    2. 为什么要修改系统的resume和suspend方法?
      简答:归根结底AFN是想在 NSURLSessionDataTask 被调用了 resumesuspend 方法时发送一次通知。

    3. 为什么需要通知?通知有什么用?
      答:如果想使用AFNetworkingTaskDidResumeNotification来通知各种UI控件当前网络任务状态为resume,那么就得调用taskDidResume:函数,而想要调用taskDidResume:函数就得调用af_resume函数。

    刨根问底:_AFURLSessionTaskSwizzling

    前面这一些问题都是有历史版本原因的,AFN 2.0 以及之前的版本中使用KVO监听NSURLSessionTask的state属性。并对state的状态改变做了过滤,在datatask任务暂停的时候或者开始的时候在主线程发起通知,因为有UI操作注册了当前的监听状态通知,所以需要在主线程进行操作发送状态改变的通知。

    但是在使用过程中开始出现崩溃,有的是在嵌套请求网络请求的时出现了崩溃,有的是并发了多个网络请求。

    为了解决KVO观察state出现的崩溃问题,后续版本开始增加了NSURLSessionDataTask的分类 (_AFStateObserving) 并在+load方法中采用methodswizzle解决此问题。

    但是iOS7和iOS8中 NSURLSessionDataTask 的 api 对于resume和suspend的实现不一样,并且自iOS9一直到现在的iOS13都存在实现不一样的问题,具体如下:

    iOS 7 : __NSCFLocalSessionTask、__NSCFURLSessionTask实现不一样,所以这两个都要进行交换
    iOS 8 : NSURLSessionTask,只需要交换这一个类的即可
    iOS 9-13 : __NSCFURLSessionTask、NSURLSessionTask,交换两个类

    NSURLSessionDataTask继承关系:

    iOS 7 : __NSCFLocalDataTask => __NSCFLocalSessionTask => __NSCFURLSessionTask
    iOS 8 : __NSCFLocalDataTask => __NSCFLocalSessionTask => NSURLSessionTask
    iOS 9-13 : __NSCFLocalDataTask => __NSCFLocalSessionTask => __NSCFURLSessionTask => NSURLSessionTask

    因此为了解决以上的问题,增加了__AFURLSessionTaskSwizzling类。_AFURLSessionTaskSwizzling类使用+load做方法替换,是从__NSCFLocalDataTask开始,按照继承链向上查找。每一个实现resume的类都进行一次检查。只要和父类的resume不一样就替换成af_resume。af自己的af_resume就是加了state的判断,在调用系统的resume之前,保存state,然后强行通知DidResume。

    最后的总结:

    AFN最初的目的是当dataTask的State状态改变时向外部发送一个通知(Notification),一开始的写法是利用KVO观察dataTask的State状态改变。

    但是KVO使用过程中出现了崩溃,就改为了MethodSwizzing。但是但是因为iOS 7、8、9...在task类的继承链中开始出现resume实现不一致,因此出现了现在的写法,按照继承链逐层向上检查。

    如果有兴趣验证我的说法是否正确,可以参考AFN官方 issue 地址:


    AFURLSessionManagerTaskDelegate 部分

    这里填前面的坑:

    • AFURLSessionManager 部分 => initWithSessionConfiguration: 方法 => 重点!!! => addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler:方法 => 整体逻辑 => 第1条

    前面的是说到下面的调用:

    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
    

    initWithTask:方法

    - (instancetype)initWithTask:(NSURLSessionTask *)task {
        self = [super init];
        if (!self) {
            return nil;
        }
        
        _mutableData = [NSMutableData data];
        _uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
        _downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
        
        __weak __typeof__(task) weakTask = task;
        for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ])
        {
            progress.totalUnitCount = NSURLSessionTransferSizeUnknown;
            progress.cancellable = YES;
            progress.cancellationHandler = ^{
                [weakTask cancel];
            };
            progress.pausable = YES;
            progress.pausingHandler = ^{
                [weakTask suspend];
            };
    #if AF_CAN_USE_AT_AVAILABLE
            if (@available(iOS 9, macOS 10.11, *))
    #else
            if ([progress respondsToSelector:@selector(setResumingHandler:)])
    #endif
            {
                progress.resumingHandler = ^{
                    [weakTask resume];
                };
            }
            
            [progress addObserver:self
                       forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                          options:NSKeyValueObservingOptionNew
                          context:NULL];
        }
        return self;
    }
    


    整体逻辑

    1. 创建NSMutableData,保存到属性_mutableData
    2. 创建NSProgress,保存到属性_uploadProgress
    3. 创建NSProgress,保存到属性_downloadProgress
    4. for in 依次配置_uploadProgress以及_downloadProgress的属性
    5. 实现了_uploadProgress_downloadProgress 的三个block,分别是取消(cancellationHandler)、暂停(pausingHandler)和开始(resumingHandler)。
    6. 在block块中使用task的弱引用调用对应的取消、暂停和重新开始方法。
    7. _uploadProgress_downloadProgress添加观察者,用来观察上传和下载的进度,delegate再以block的形式通知出去

    分解说明

    1. POST、GET等请求方法的uploadProgressBlockdownloadProgressBlock即上传下载的block,都是由 AFURLSessionManagerTaskDelegate 保存的。AFNetworking主线梳理(二)dataTaskWithRequest方法的解析中最后还是调用了addDelegateForDataTask。本篇文章对addDelegateForDataTask有详细的解析(往上翻)。

    2. 实现_uploadProgress_downloadProgress的三个block实际上是间接的让task去调用取消、暂停和开始。最终目的是赋予 _uploadProgress_downloadProgress 这两个进度取消、暂停和开始的权力。

    3. _uploadProgress_downloadProgress添加观察者后,在KVO 的回调observeValueForKeyPath方法中接收通知。

    4. observeValueForKeyPath方法中使用block通知开发者上传和下载的进度。

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
       if ([object isEqual:self.downloadProgress]) {
            if (self.downloadProgressBlock) {
                self.downloadProgressBlock(object);
            }
        }
        else if ([object isEqual:self.uploadProgress]) {
            if (self.uploadProgressBlock) {
                self.uploadProgressBlock(object);
            }
        }
    }
    

    AFJSONResponseSerializer 部分

    填前面的坑:
    AFHTTPSessionManager 部分 => initWithBaseURL:sessionConfiguration: 方法 => 小结 => 第2条AFHTTPRequestSerializer 部分 解析到了 self.responseSerializer = [AFJSONResponseSerializer serializer]; 这句代码,本部分就从 + serializer 方法开始。

    + serializer 方法

    @implementation AFJSONResponseSerializer
    
    + (instancetype)serializer {
        return [self serializerWithReadingOptions:(NSJSONReadingOptions)0];
    }
    
    + (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions {
        AFJSONResponseSerializer *serializer = [[self alloc] init];
        serializer.readingOptions = readingOptions;
    
        return serializer;
    }
    
    - (instancetype)init {
        self = [super init];
        if (!self) {
            return nil;
        }
    
        self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];
    
        return self;
    }
    
    ...省略...
    
    @end
    


    整体逻辑

    1. + serializer 调用 + serializerWithReadingOptions:

    2. + serializerWithReadingOptions: 调用 [[self alloc] init]

    3. - init 调用 [super init],调用父类 AFHTTPResponseSerializer 的 - init 方法创建一个实例,并返回

      @implementation AFHTTPResponseSerializer
      
      - (instancetype)init {
          self = [super init];
          if (!self) {
              return nil;
          }
      
          self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
          self.acceptableContentTypes = nil;
      
          return self;
      }
      
    4. 设置三种默认的ContentType,并保存到acceptableContentTypes属性

      • "application/json"
      • "text/json"
      • "text/javascript"
    5. - init返回 serializer 实例,回到+ serializerWithReadingOptions:

    6. serializer 保存 readingOptions 到属性 readingOptions

    7. + serializerWithReadingOptions:返回 serializer 实例,回到+ serializer

    8. 返回 AFJSONResponseSerializer 实例对象

    AFJSONResponseSerializer 以及其父类 AFHTTPRequestSerializer 的初始化很简单,只是赋了一些默认值。


    AFHTTPRequestSerializer 部分

    填前面的坑:
    AFHTTPSessionManager 部分 => initWithBaseURL:sessionConfiguration: 方法 => 小结 => 第3条AFJSONResponseSerializer 部分解析到了 self.requestSerializer = [AFHTTPRequestSerializer serializer]; 这句代码,本部分就从 + serializer 方法开始。

    @implementation AFHTTPRequestSerializer
    
    + (instancetype)serializer {
        return [[self alloc] init];
    }
    
    - (instancetype)init {
        self = [super init];
        if (!self) {
            return nil;
        }
        // 设置默认的字符串编码
        self.stringEncoding = NSUTF8StringEncoding;
        // 初始化self.mutableHTTPRequestHeaders
        self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary];
        // 初始化self.requestHeaderModificationQueue并发队列
        self.requestHeaderModificationQueue = dispatch_queue_create("requestHeaderModificationQueue", DISPATCH_QUEUE_CONCURRENT);
    
        // 设置可接受的语言,最多接受5种语言,q是权重。"%@;q=%0.1g"是通用标准的写法。
        NSMutableArray *acceptLanguagesComponents = [NSMutableArray array];
        [[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            float q = 1.0f - (idx * 0.1f);
            [acceptLanguagesComponents addObject:[NSString stringWithFormat:@"%@;q=%0.1g", obj, q]];
            *stop = q <= 0.5f;
        }];
        // 这里是第一次被赋默认值:Accept-Language
        [self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"];
        // 配置 userAgent
        NSString *userAgent = nil;
    #if TARGET_OS_IOS
        // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
        userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]];
    #elif TARGET_OS_WATCH
        // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
        userAgent = [NSString stringWithFormat:@"%@/%@ (%@; watchOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[WKInterfaceDevice currentDevice] model], [[WKInterfaceDevice currentDevice] systemVersion], [[WKInterfaceDevice currentDevice] screenScale]];
    #elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
        userAgent = [NSString stringWithFormat:@"%@/%@ (Mac OS X %@)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[NSProcessInfo processInfo] operatingSystemVersionString]];
    #endif
        if (userAgent) {
            if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) {
                NSMutableString *mutableUserAgent = [userAgent mutableCopy];
                if (CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, (__bridge CFStringRef)@"Any-Latin; Latin-ASCII; [:^ASCII:] Remove", false)) {
                    userAgent = mutableUserAgent;
                }
            }
            // 这里是第二次被赋默认值:User-Agent
            [self setValue:userAgent forHTTPHeaderField:@"User-Agent"];
        }
    
        // 设置默认的请求方法
        self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil];
        // 给6个特定属性添加键值观察(KVO)
        self.mutableObservedChangedKeyPaths = [NSMutableSet set];
        for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
            if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
                [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
            }
        }
    
        return self;
    }
    


    整体逻辑

    1. 设置默认的字符串编码为 NSUTF8StringEncoding

    2. 初始化属性 self.mutableHTTPRequestHeaders,一个可变字典。

    3. 初始化 self.requestHeaderModificationQueue,一个并发队列。

      对于requestHeaderModificationQueue并发队列,AFNetworking主线梳理(二)AFHTTPRequestSerializer 部分 => requestWithMethod:URLString:parameters:error: 方法解析 => 第二部分 (高能预警)=> self.HTTPRequestHeaders部分 => 分解说明 => 第2、3、4条有详细的使用场景。

    4. 组装 Accept-Language 的信息,AFN 的逻辑是最多接受5种语言,q是语言优先的权重。"%@;q=%0.1g"是通用标准的格式。

    5. self.mutableHTTPRequestHeaders属性第一次设置默认值:Accept-Language。self.mutableHTTPRequestHeaders通过setValue:forHTTPHeaderField:方法间接赋值。

      // AFHTTPRequestSerializer.h
      - (void)setValue:(nullable NSString *)value forHTTPHeaderField:(NSString *)field;
      
      // AFHTTPRequestSerializer.m
      - (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field
      {
        dispatch_barrier_async(self.requestHeaderModificationQueue, ^{
            [self.mutableHTTPRequestHeaders setValue:value forKey:field];
        });
      }
      
    6. 获取系统配置,组装 userAgent 的信息。

    7. self.mutableHTTPRequestHeaders属性第二次设置默认值:userAgent。

    8. 设置默认的请求方法。

      使用场景在 AFNetworking主线梳理(二)AFHTTPRequestSerializer 部分 => requestWithMethod:URLString:parameters:error: 方法解析 => 第二部分 (高能预警)=> 使用query装配mutableRequest部分

    9. 给6个特定属性添加键值观察(KVO)。

      具体请参考AFNetworking主线梳理(二)AFHTTPRequestSerializer 部分 => requestWithMethod:URLString:parameters:error: 方法解析 => 第一部分

    相关文章

      网友评论

        本文标题:AFNetworking主线梳理(一)

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