美文网首页
AFNetworking源码阅读(一)

AFNetworking源码阅读(一)

作者: HoooChan | 来源:发表于2018-10-29 18:32 被阅读35次

    原生请求框架

    苹果官方提供的网络请求API有两种: NSURLConnectionNSURLSession
    iOS 7之前,只有 NSURLConncetion。
    iOS 9之后,苹果官方放弃NSURLConnection,推荐使用NSURLSession。

    本文所讲的AF3.x是基于NSURLSession来封装的

    原生请求:

    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://api.douban.com/v2/movie/top250"]];
    
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *myTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (response) {
        
        }
    }];
    [myTask resume];
    

    可以看到原生的请求是通过NSURLSession创建NSURLSessionDataTask来发起的。而AFNetWork的请求也是围绕这两个来进行封装的。

    AFHTTPSessionManager

    基本的Get请求

        // url:         请求地址
        // parameters:  参数
        // headers:     头部
        // progress:    进度回调
        // success:     成功回调
        // failure:     失败回调
        AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] init];
        [manager GET:url parameters:nil headers:nil progress:^(NSProgress * _Nonnull downloadProgress) {
            
        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            
        }];
    

    基本的流程图如下:


    AFNetwork发起请求.png

    一、AFHTTPDESessionManager的初始化:

    其实AFHTTPSessionManager是继承于AFURLSessionManager的,只做了一些简单的封装,请求的逻辑主要由父类AFURLSessionManager来完成。初始化AFHTTPSessionManager,触发父类AFURLSessionManager的初始化方法。

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

    最后都来到- (instancetype)initWithBaseURL:(NSURL *)url sessionConfiguration:(NSURLSessionConfiguration *)configuration方法,主要做了三件事:

    1、对传过来的baseUrl进行处理,如果有值且最后不包含“/”,url加上“/”
    2、保存baseURL
    3、 生成了一个请求序列对象和一个响应序列对象

    剩下的的主要是父类的初始化:

    - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
        self = [super init];
        if (!self) {
            return nil;
        }
    
        if (!configuration) {
            configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        }
    
        self.sessionConfiguration = configuration;
    
        self.operationQueue = [[NSOperationQueue alloc] init];
        // 让回调的代理queue是串行的,即请求完成的task只能一个个被回调
        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;
    
        // 清空session中所有的task
        [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、 创建NSURLSession

    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
    
    • sessionConfiguration可以由外部传进来;
    • NSURLSession的代理设置为AFURLSessionManager本身;
    • delegateQueue是代理回调的queue。operationQueue通过self.operationQueue.maxConcurrentOperationCount = 1;保证回调的代理queue是串行的,即请求完成的task只能一个个被回调。

    这里设置maxConcurrentOperationCount为1,一部分的原因是因为AF2.x所有的回调是在一条线程,这条线程是AF的常驻线程,这里可能是为了和之前版本保持一致。另外因为代理的相关操作AF都使用了NSLock,所以就算Queue的并发数设置为n,因为多线程回调,锁的等待,导致所提升的程序速度也并不明显。反而多task回调导致的多线程并发,平白浪费了部分性能。AFNetworking到底做了什么?(二)

    2、初始化自定义代理和task的映射self.mutableTaskDelegatesKeyedByTaskIdentifier,task本身的代理都会被转发到对应的AF自定义代理

    3、清空session中所有的task的代理和回调
    通过[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { }];来获取所有未完成的task。

    按理说初始化的时候session里面应该没有task,这里是为了防止为了防止后台回来,重新初始化这个session,一些之前的后台请求任务,导致程序的crash

    如果是BackgroundSession,当加入了多个Task,程序切到后台,在还有Task还没有下载完的时候关掉强制退出程序,然后再进入程序的时候,如果生成Session的NSURLSessionConfiguration的ID和之前的相同,马上又可以收到上一次关闭程序之前没有汇报工作的Task的结束情况(成功或者失败)。

    至此初始化完成。

    以下是GET网络请求:

    二、生成NSURLSessionDataTask实例

    调用GET方法,发起Get请求,首先要创建NSURLSessionDataTask

    - (NSURLSessionDataTask *)GET:(NSString *)URLString
                       parameters:(id)parameters
                          headers:(nullable NSDictionary <NSString *, NSString *> *)headers
                         progress:(void (^)(NSProgress * _Nonnull))downloadProgress
                          success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
                          failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
    {
        
        NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                            URLString:URLString
                                                           parameters:parameters
                                                              headers:headers
                                                       uploadProgress:nil
                                                     downloadProgress:downloadProgress
                                                              success:success
                                                              failure:failure];
        
        [dataTask resume];
        
        return dataTask;
    }
    
    - (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                           URLString:(NSString *)URLString
                                          parameters:(id)parameters
                                             headers:(NSDictionary <NSString *, NSString *> *)headers
                                      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;
        // 序列化请求(拼接请求参数)
        NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
        for (NSString *headerField in headers.keyEnumerator) {
            [request addValue:headers[headerField] forHTTPHeaderField:headerField];
        }
        // 序列化出错
        if (serializationError) {
            if (failure) {
                dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                    failure(nil, serializationError);
                });
            }
    
            return nil;
        }
    
        __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;
    }
    

    主要工作:
    1、用self.requestSerializer和各种参数去生成一个我们最终请求网络需要的NSMutableURLRequest实例。
    2、调用另外一个方法dataTaskWithRequest去拿到我们最终需要的NSURLSessionDataTask实例,并且在完成的回调里,调用我们传过来的成功和失败的回调。

    requestSerializer方法 :

    self.requestSerialiizer是AFURLRequestSerialization的实例,实在初始化AFHTTPSessionManager时创建的self.requestSerializer = [AFHTTPRequestSerializer serializer];

    - (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                     URLString:(NSString *)URLString
                                    parameters:(id)parameters
                                         error:(NSError *__autoreleasing *)error
    {
        NSParameterAssert(method);
        NSParameterAssert(URLString);
    
        NSURL *url = [NSURL URLWithString:URLString];
    
        NSParameterAssert(url);
    
        NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
        mutableRequest.HTTPMethod = method;
    
        // 设置请求头
        for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
            if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
                [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
            }
        }
    
        // 拼接请求参数
        mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
    
        return mutableRequest;
    }
    

    这个函数主要是设置请求的类型、设置头部以及拼接请求参数。

    static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
        static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))];
        });
    
        return _AFHTTPRequestSerializerObservedKeyPaths;
    }
    

    AFHTTPRequestSerializerObservedKeyPaths()函数主要是创建了一个单例,里面封装了一些属性的名字。
    self.mutableObservedChangedKeyPaths是在self.requestSerializer初始化的时候创建的:

        self.mutableObservedChangedKeyPaths = [NSMutableSet set];
        for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
            if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
                [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
            }
        }
    
    - (void)observeValueForKeyPath:(NSString *)keyPath
                          ofObject:(__unused id)object
                            change:(NSDictionary *)change
                           context:(void *)context
    {
        if (context == AFHTTPRequestSerializerObserverContext) {
            if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
                [self.mutableObservedChangedKeyPaths removeObject:keyPath];
            } else {
                [self.mutableObservedChangedKeyPaths addObject:keyPath];
            }
        }
    }
    

    当观察到这些set方法被调用了,而且不为Null就会添加到集合里,否则移除。所以self.mutableObservedChangedKeyPaths其实就是我们自己设置的request属性值的集合。

    接下来就是拼接参数:

    - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                                   withParameters:(id)parameters
                                            error:(NSError *__autoreleasing *)error
    {
        NSParameterAssert(request);
    
        NSMutableURLRequest *mutableRequest = [request mutableCopy];
    
        // 设置头部
        [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
            if (![request valueForHTTPHeaderField:field]) {
                [mutableRequest setValue:value forHTTPHeaderField:field];
            }
        }];
    
        NSString *query = nil;
        if (parameters) {
            if (self.queryStringSerialization) {
                NSError *serializationError;
                query = self.queryStringSerialization(request, parameters, &serializationError);
    
                if (serializationError) {
                    if (error) {
                        *error = serializationError;
                    }
    
                    return nil;
                }
            } else {
                switch (self.queryStringSerializationStyle) {
                    case AFHTTPRequestQueryStringDefaultStyle:
                        query = AFQueryStringFromParameters(parameters);
                        break;
                }
            }
        }
    
        if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
            if (query && query.length > 0) {
                mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
            }
        } else {
            // #2864: an empty string is a valid x-www-form-urlencoded payload
            if (!query) {
                query = @"";
            }
            if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
                [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
            }
            [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
        }
    
        return mutableRequest;
    }
    

    主要做了三件事:
    1、从self.HTTPRequestHeaders中拿到设置的参数,赋值到请求的request里去
    2、把请求网络的参数,从array dic set这些容器类型转换为字符串
    3、根据该request的请求类型,来判断参数字符串应该如何设置到request中去。如果是GET、HEAD、DELETE,则把参数quey是拼接到url后面的。而POST、PUT是把query拼接到http body中的

    拼接参数:

    NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
        NSMutableArray *mutablePairs = [NSMutableArray array];
        for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
            [mutablePairs addObject:[pair URLEncodedStringValue]];
        }
    
        return [mutablePairs componentsJoinedByString:@"&"];
    }
    
    NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
        return AFQueryStringPairsFromKeyAndValue(nil, dictionary);
    }
    
    NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
        NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
    
        NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
    
        if ([value isKindOfClass:[NSDictionary class]]) {
            NSDictionary *dictionary = value;
            // Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
            for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
                id nestedValue = dictionary[nestedKey];
                if (nestedValue) {
                    [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
                }
            }
        } else if ([value isKindOfClass:[NSArray class]]) {
            NSArray *array = value;
            for (id nestedValue in array) {
                [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
            }
        } else if ([value isKindOfClass:[NSSet class]]) {
            NSSet *set = value;
            for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
                [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
            }
        } else {
            [mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
        }
    
        return mutableQueryStringComponents;
    }
    

    至此创建了NSMutableURLRequest

    接下来到AFURLSessionManager中创建NSURLSessionDataTask

    - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                                   uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                 downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                                completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject,  NSError * _Nullable error))completionHandler {
    
        __block NSURLSessionDataTask *dataTask = nil;
        url_session_manager_create_task_safely(^{
            // 调用session的方法来创建request
            dataTask = [self.session dataTaskWithRequest:request];
        });
    
        [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
    
        return dataTask;
    }
    

    这里并不是直接创建,而是传入一个block到url_session_manager_create_task_safely方法

    static void url_session_manager_create_task_safely(dispatch_block_t _Nonnull block) {
        if (block != NULL) {
            if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) {
                // Fix of bug
                // Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8)
                // Issue about:https://github.com/AFNetworking/AFNetworking/issues/2093
                // 第一件事,创建NSURLSessionDataTask,里面适配了Ios8以下taskIdentifiers,函数创建task对象。
                // 其实现应该是因为iOS 8.0以下版本中会并发地创建多个task对象,而同步有没有做好,导致taskIdentifiers 不唯一,这边做了一个串行处理
                //理解下,第一为什么用sync,因为是想要主线程等在这,等执行完,在返回,因为必须执行完dataTask才有数据,传值才有意义。
                //第二,为什么要用串行队列,因为这块是为了防止ios8以下内部的dataTaskWithRequest是并发创建的,这样会导致taskIdentifiers这个属性值不唯一,因为后续要用taskIdentifiers来作为Key对应delegate。
                dispatch_sync(url_session_manager_creation_queue(), block);
            } else {
                block();
            }
        }
    }
    

    iOS8以下task创建,taskIdentifiers不唯一。而这个属性是我们之后添加代理的key,它必须是唯一的。
    所以这里做了一个判断,如果是iOS8以下,则用串行同步的方式去执行这个Block,也就是创建session。否则直接执行。
    url_session_manager_creation_queue()的到一个单例的串行队列

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

    这里使用disptach_sync

    disptach_sync是同步扔一个block到queue中,即扔了就等着,等到queue排队把这个block执行完了之后,才继续执行下一行代码。

    之后就是给每个task创建并对应一个AF的代理对象。

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

    在这里生成AF自定义的Delegate,我们传进来的参数都赋值给这个AF代理了。然后调用了[self setDelegate:delegate forTask:dataTask];

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

    这个方法主要就是把AF代理和task建立映射,存在了一个我们事先声明好的字典里。加锁保证线程安全。
    另外对任务添加监听。

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

    到这里对task的处理就结束了。

    三、发起请求并处理结果

    回到最初初始化时创建session代码:

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

    AFURLSessionManager作为所有task的delegate,可以看看它实现的协议:
    @interface AFURLSessionManager : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying>

    分别为NSURLSessionDelegateNSURLSessionTaskDelegateNSURLSessionDataDelegateNSURLSessionDownloadDelegate
    NSURLSession的初始化+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue;只设置了NSURLSessionDelegate一种代理,但其实上面这四种协议都是有继承关系的:

    @protocol NSURLSessionDelegate <NSObject>
    @protocol NSURLSessionTaskDelegate <NSURLSessionDelegate>
    @protocol NSURLSessionDataDelegate <NSURLSessionTaskDelegate>
    @protocol NSURLSessionDownloadDelegate <NSURLSessionTaskDelegate>
    @protocol NSURLSessionStreamDelegate <NSURLSessionTaskDelegate>
    

    在NSURLSession实现中,只要设置了这个代理,它会去判断这些所有的代理,是否respondsToSelector这些代理中的方法,如果响应了就会去调用。

    而AF还重写了respondsToSelector方法:

    - (BOOL)respondsToSelector:(SEL)selector {
        if (selector == @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)) {
            return self.taskWillPerformHTTPRedirection != nil;
        } else if (selector == @selector(URLSession:dataTask:didReceiveResponse:completionHandler:)) {
            return self.dataTaskDidReceiveResponse != nil;
        } else if (selector == @selector(URLSession:dataTask:willCacheResponse:completionHandler:)) {
            return self.dataTaskWillCacheResponse != nil;
        }
    #if !TARGET_OS_OSX
        else if (selector == @selector(URLSessionDidFinishEventsForBackgroundURLSession:)) {
            return self.didFinishEventsForBackgroundURLSession != nil;
        }
    #endif
    
        return [[self class] instancesRespondToSelector:selector];
    }
    

    这样如果没实现这些我们自定义的Block也不会去回调这些代理。因为本身某些代理,只执行了这些自定义的Block,如果Block都没有赋值,那我们调用代理也没有任何意义。

    NSURLSessionDataDelegate
    - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data;

    这个方法和上面didCompleteWithError算是NSUrlSession的代理中最重要的两个方法了。当我们获取到数据就会调用,会被反复调用,请求到的数据就在这被拼装完整。

    AF的实现:

    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
        didReceiveData:(NSData *)data
    {
    
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
        [delegate URLSession:session dataTask:dataTask didReceiveData:data];
    
        if (self.dataTaskDidReceiveData) {
            self.dataTaskDidReceiveData(session, dataTask, data);
        }
    }
    
    - (void)URLSession:(__unused NSURLSession *)session
              dataTask:(__unused NSURLSessionDataTask *)dataTask
        didReceiveData:(NSData *)data
    {
        self.downloadProgress.totalUnitCount = dataTask.countOfBytesExpectedToReceive;
        self.downloadProgress.completedUnitCount = dataTask.countOfBytesReceived;
    
        [self.mutableData appendData:data];
    }
    

    NSURLSessionTaskDelegate

    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error;

    这个代理就是task完成了的回调。

    看看AFURLSessionMannager的实现:

    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
    didCompleteWithError:(NSError *)error
    {
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
    
        // delegate may be nil when completing a task in the background
        if (delegate) {
            //把代理转发给我们绑定的delegate
            [delegate URLSession:session task:task didCompleteWithError:error];
            //转发完移除delegate
            [self removeDelegateForTask:task];
        }
    
        if (self.taskDidComplete) {
            self.taskDidComplete(session, task, error);
        }
    }
    

    根据task拿到代理:

    - (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
        NSParameterAssert(task);
    
        AFURLSessionManagerTaskDelegate *delegate = nil;
        [self.lock lock];
        delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
        [self.lock unlock];
    
        return delegate;
    }
    

    在自定义代理中的处理

    - (void)URLSession:(__unused NSURLSession *)session
                  task:(NSURLSessionTask *)task
    didCompleteWithError:(NSError *)error
    {
        __strong AFURLSessionManager *manager = self.manager;
    
        __block id responseObject = nil;
    
        __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
        userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
    
        //Performance Improvement from #2672
        NSData *data = nil;
        if (self.mutableData) {
            data = [self.mutableData copy];
            //We no longer need the reference, so nil it out to gain back some memory.
            self.mutableData = nil;
        }
    
    #if AF_CAN_USE_AT_AVAILABLE && AF_CAN_INCLUDE_SESSION_TASK_METRICS
        if (@available(iOS 10, macOS 10.12, watchOS 3, tvOS 10, *)) {
            if (self.sessionTaskMetrics) {
                userInfo[AFNetworkingTaskDidCompleteSessionTaskMetrics] = self.sessionTaskMetrics;
            }
        }
    #endif
    
        if (self.downloadFileURL) {
            userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
        } else if (data) {
            userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
        }
    
        if (error) {
            userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
    
            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(), ^{
                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(), ^{
                    if (self.completionHandler) {
                        self.completionHandler(task.response, responseObject, serializationError);
                    }
    
                    dispatch_async(dispatch_get_main_queue(), ^{
                        [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
                    });
                });
            });
        }
    }
    
    • 1、生成一个存储这个task相关信息的字典userinfo,这个字典用来作为发送任务完成的通知的参数
    • 2、如果任务成功,则到一个并行队列中解析数据;
    • 3、数据解析完成后,如果自定义了GCD完成组completionGroup和完成队列的话completionQueue,会在加入这个组和在队列中回调Block。否则默认的是AF的创建的组。

    NSURLSessionDelegate的源码

    @protocol NSURLSessionDelegate <NSObject>
    @optional
    
    - (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error;
    
    - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
                                                 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler;
    
    - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session API_AVAILABLE(ios(7.0), watchos(2.0), tvos(9.0)) API_UNAVAILABLE(macos);
    
    @end
    

    - (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error:

    当这个session已经失效时,该代理方法被调用。

    • 如果使用finishTasksAndInvalidate函数使该session失效,那么session首先会先完成最后一个task,然后再调用URLSession:didBecomeInvalidWithError:代理方法,
    • 如果你调用invalidateAndCancel方法来使session失效,那么该session会立即调用上面的代理方法。

    AFURLSerrionManager中此方法的实现:

    - (void)URLSession:(NSURLSession *)session
    didBecomeInvalidWithError:(NSError *)error
    {
        if (self.sessionDidBecomeInvalid) {
            self.sessionDidBecomeInvalid(session, error);
        }
    
        // 当一个session无效时,post名为AFURLSessionDidInvalidateNotification的Notification
        [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
    }
    

    - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler;):

    Web服务器接收到客户端请求时,有时候需要先验证客户端是否为正常用户,再决定是够返回真实数据。这种情况称之为服务端要求客户端接收挑战NSURLAuthenticationChallenge *challenge

    接收到挑战后,客户端要根据服务端传来的challenge来生成completionHandler所需的NSURLSessionAuthChallengeDisposition dispositionNSURLCredential *credential(disposition指定应对这个挑战的方法,而credential是客户端生成的挑战证书,注意只有challenge中认证方法为NSURLAuthenticationMethodServerTrust的时候,才需要生成挑战证书)。最后调用completionHandler回应服务器端的挑战。

    • 当服务器端要求客户端提供证书时或者进行NTLM认证(Windows NT LAN Manager,微软提出的WindowsNT挑战/响应验证机制)时,此方法允许你的app提供正确的挑战证书。
    • 当某个session使用SSL/TLS协议,第一次和服务器端建立连接的时候,服务器会发送给iOS客户端一个证书,此方法允许你的app验证服务期端的证书链(certificate keychain)
      如果没有实现该方法,该session会调用其NSURLSessionTaskDelegate的代理方法URLSession:task:didReceiveChallenge:completionHandler: 。

    - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session API_AVAILABLE(ios(7.0), watchos(2.0), tvos(9.0)) API_UNAVAILABLE(macos);:

    当session中所有已经入队的消息被发送出去后,会调用该代理方法。

    在iOS中,当一个后台传输任务完成或者后台传输时需要证书,而此时你的app正在后台挂起,那么你的app在后台会自动重新启动运行,并且这个app的UIApplicationDelegate会发送一个application:handleEventsForBackgroundURLSession:completionHandler:消息。该消息包含了对应后台的session的identifier,而且这个消息会导致你的app启动。你的app随后应该先存储completion handler,然后再使用相同的identifier创建一个background configuration,并根据这个background configuration创建一个新的session。这个新创建的session会自动与后台任务重新关联在一起。

    当你的app获取了一个URLSessionDidFinishEventsForBackgroundURLSession:消息,这就意味着之前这个session中已经入队的所有消息都转发出去了,这时候再调用先前存取的completion handler是安全的,或者因为内部更新而导致调用completion handler也是安全的。

    参考

    相关文章

      网友评论

          本文标题:AFNetworking源码阅读(一)

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