AFNetworking主线梳理(一)

全篇使用的案例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: 方法解析 => 第一部分

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270