美文网首页
AFNetWorking浅析

AFNetWorking浅析

作者: 三国韩信 | 来源:发表于2020-07-20 23:42 被阅读0次

    一切的一切要从5百万年前的赛博坦星球大战开始说起,当时我第一次写iOS代码.....错了,重来!啊哈哈哈哈,一切的一切要从一道面试题说起:

    AFNetworking 2x版本为什么有一条常驻的线程,作用是啥?3x版本为什么没有?
    

    要回答这道题,必然要对AFNetWorking的2x、3x版本都有所了解。所以下面来解析一波AFNetWorking到底做了啥,从3x解析起吧

    AFNetWorking 3x

    通过代码结构可以看出,除去Support Files,可以看到AF分为如下5个功能模块:

    • 网络通信模块(AFURLSessionManager、AFHTTPSessionManger)
    • 网络状态监听模块(Reachability)
    • 网络通信安全策略模块(Security)
    • 网络通信信息序列化/反序列化模块(Serialization)
    • 对于iOS UIKit库的扩展(UIKit)
    AFNetWorking

    其核心当然是网络通信模块AFURLSessionManager

    模块结构图

    其中AFHTTPSessionManager是继承于AFURLSessionManager的,我们一般做网络请求都是用这个类,但是它本身是没有做实事的,只是做了一些简单的封装,把请求逻辑分发给父类AFURLSessionManager或者其它类去做。showCode:

    - (instancetype)init {
        return [self initWithSessionConfiguration:nil];
    }
    
    - (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并发线程数设置为1,NSURLSession的代理回调会来到这个queue,因为并发设置为1,所以是个串行的队列。
        self.operationQueue.maxConcurrentOperationCount = 1;
    //注意代理,代理的继承,实际上NSURLSession去判断了,你实现了哪个方法会去调用,包括子代理的方法!
        self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
       //各种响应转码
        self.responseSerializer = [AFJSONResponseSerializer serializer];
       //安全策略设置
        self.securityPolicy = [AFSecurityPolicy defaultPolicy];
        
        // 设置存储NSURL task与AFURLSessionManagerTaskDelegate的dic(重点,在AFNet中,每一个task都会被匹配一个AFURLSessionManagerTaskDelegate 来做task的delegate事件处理)
        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;
    }
    

    这个是AFURLSessionManager的初始化方法。
    那么发送一个get请求的流程是啥?它是调用了AFHTTPSessionManager类中的方法,先去序列化生产对应的NSMutableURLRequest,然后调用父类AFURLSessionManager的方法来发送请求的。

    - (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                           URLString:(NSString *)URLString
                                          parameters:(id)parameters
                                      uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
                                    downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                                             success:(void (^)(NSURLSessionDataTask *, id))success
                                             failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
    {
        NSError *serializationError = nil;
        NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
        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;
    }
    

    那么它生成NSMutableURLRequest 是调用了AFURLRequestSerialization中的方法。

    - (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];
            }
        }
        // 将传入的parameters进行编码,并添加到request中,也就是把入参进行序列化,然后拼到url后面或者放到body体里
        mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
    
        return mutableRequest;
    }
    
    - (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;
               // 自定义的编码方式对入参编码   (block的方式定义编码自己的规则)
                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;
    }
    

    那么默认的编码方式是如何的呢?

    NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
        NSMutableArray *mutablePairs = [NSMutableArray array];
        for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
            [mutablePairs addObject:[pair URLEncodedStringValue]];
        }
        // 把单个的入参编码后的字符串用&拼接起来。
        return [mutablePairs componentsJoinedByString:@"&"];
    }
    
    - (NSString *)URLEncodedStringValue {
        if (!self.value || [self.value isEqual:[NSNull null]]) {
            return AFPercentEscapedStringFromString([self.field description]);
        } else {
            // 最后编码成的字符串的方式,类似key=value的这种字符串
            return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedStringFromString([self.field description]), AFPercentEscapedStringFromString([self.value description])];
        }
    }
    

    比如编码的结果如下:

    假如请求的入参是这样的字典:
    @{ 
         @"name" : @"bang", 
         @"phone": @"13444444444", 
         @"families": @"myFamilies",
     
    } 
    
    编码后的结果:name=bang&phone=13444444444&families=myFamilies
    

    紧接着这个方法还根据该request中请求类型,来判断参数字符串应该如何设置到request中去。如果是GET、HEAD、DELETE,则把参数quey是拼接到url后面的。而POST、PUT是把query拼接到http body中的。
    至此,我们生成了一个request。

    开始请求数据

    有了request后,我们接着调用了父类的生成task的方法,并且执行了一个成功和失败的回调,我们接着去父类AFURLSessionManger里看(总算到我们的核心类了..)

    - (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;
        //第一件事,创建NSURLSessionDataTask,里面适配了Ios8以下taskIdentifiers,函数创建task对象。
        //其实现应该是因为iOS 8.0以下版本中会并发地创建多个task对象,而同步有没有做好,导致taskIdentifiers 不唯一…这边做了一个串行处理
        url_session_manager_create_task_safely(^{
            dataTask = [self.session dataTaskWithRequest:request];
        });
    
        [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
    
        return dataTask;
    }
    
    - (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,这个其实就是AF的自定义代理。我们请求传来的参数,都赋值给这个AF的代理了。
        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;
    }
    
    - (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
                forTask:(NSURLSessionTask *)task
    {
        NSParameterAssert(task);
        NSParameterAssert(delegate);
    
        [self.lock lock];
         // 将AF delegate放入以taskIdentifier标记的词典中(同一个NSURLSession中的taskIdentifier是唯一的)
        self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
        [self addNotificationObserverForTask:task];
        [self.lock unlock];
    }
    

    AFURLSessionManagerTaskDelegate这个类主要是用来处理一些回调的。NSURLSession的代理回调方法中,有一些方法是回调到这个类中去处理的。因为上面的方法把AF代理和task建立映射,存在了一个我们事先声明好的字典里。那么针对不同的请求task的回调,就可以不一定的去处理。就是放在和task一一对应的AFURLSessionManagerTaskDelegate类中去做的。而对于共性的那些代理回调就在本类中去处理。
    而要加锁的原因是因为本身我们这个字典属性是mutable的,是线程不安全的。而我们对这些方法的调用,确实是会在复杂的多线程环境中,后面会仔细提到线程问题。
    到这里我们整个对task的处理就完成了。

    开始处理回调方法。

    这里简述一下NSURLSession的各个代理方法具体是干啥的。

    - (void)URLSession:(NSURLSession *)session
    didBecomeInvalidWithError:(NSError *)error{}
    - (void)URLSession:(NSURLSession *)session
    didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
     completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
    {}
    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
    willPerformHTTPRedirection:(NSHTTPURLResponse *)response
            newRequest:(NSURLRequest *)request
     completionHandler:(void (^)(NSURLRequest *))completionHandler
    {}
    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
    didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
     completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
    {}
    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
     needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
    {}
    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
       didSendBodyData:(int64_t)bytesSent
        totalBytesSent:(int64_t)totalBytesSent
    totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
    {}
    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
    didCompleteWithError:(NSError *)error
    {}
    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveResponse:(NSURLResponse *)response
     completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
    {}
    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
    didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
    {}
    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
        didReceiveData:(NSData *)data
    {}
    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
     willCacheResponse:(NSCachedURLResponse *)proposedResponse
     completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler
    {}
    - (void)URLSession:(NSURLSession *)session
          downloadTask:(NSURLSessionDownloadTask *)downloadTask
    didFinishDownloadingToURL:(NSURL *)location
    {}
    
    - (void)URLSession:(NSURLSession *)session
          downloadTask:(NSURLSessionDownloadTask *)downloadTask
          didWriteData:(int64_t)bytesWritten
     totalBytesWritten:(int64_t)totalBytesWritten
    totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
    {}
    - (void)URLSession:(NSURLSession *)session
          downloadTask:(NSURLSessionDownloadTask *)downloadTask
     didResumeAtOffset:(int64_t)fileOffset
    expectedTotalBytes:(int64_t)expectedTotalBytes
    {}
    
    

    在上面的的这些回调方法中,有几个是特别的,是回调到AFURLSessionManagerTaskDelegate这个类中来处理的。这里列举其中的2个

      // 这个是当请求结束的时候会来到的函数,成功与失败都会来到这。
    - (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 (self.downloadFileURL) {
            userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
        } else if (data) {
            userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
        }
    
        if (error) {     // 请求报错了的处理
            userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
          //可以自己自定义完成组 和自定义完成queue,完成回调,不然就是主线程去回调。
            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 {
             // url_session_manager_processing_queue AF的并行队列
            dispatch_async(url_session_manager_processing_queue(), ^{
              // 请求有数据成功返回,当也有可能返回的数据格式不对,解析失败的请情况。
                NSError *serializationError = nil;
             // 解析数据
                responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
               //如果是下载文件,那么responseObject为下载的路径
                if (self.downloadFileURL) {
                    responseObject = self.downloadFileURL;
                }
                 //写入userInfo
                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];
                    });
                });
            });
        }
    }
    
    - (void)URLSession:(__unused NSURLSession *)session
              dataTask:(__unused NSURLSessionDataTask *)dataTask
        didReceiveData:(NSData *)data
    {
        // 接受到数据,通过self.mutableData存起来。
        self.downloadProgress.totalUnitCount = dataTask.countOfBytesExpectedToReceive;
        self.downloadProgress.completedUnitCount = dataTask.countOfBytesReceived;
    
        [self.mutableData appendData:data];
    }
    

    至此,数据请求回来,就能正常回调了。

    最后我们来梳理一下AF3.x的整个流程和线程的关系:
    • 我们一开始初始化sessionManager的时候,一般都是在主线程,(当然不排除有些人喜欢在分线程初始化...)
    • 然后我们调用get或者post等去请求数据,接着会进行request拼接,AF代理的字典映射,progressKVO添加等等,到NSUrlSessionresume之前这些准备工作,仍旧是在主线程中的。
    • 然后我们调用NSUrlSessionresume,接着就跑到NSUrlSession内部去对网络进行数据请求了,在它内部是多线程并发的去请求数据的。
    • 紧接着数据请求完成后,回调回来在我们一开始生成的并发数为1的NSOperationQueue中,这个时候会是多线程串行的回调回来的。(注:不明白的朋友可以看看雷纯峰大神这篇iOS 并发编程之 Operation Queues
    • 然后我们到返回数据解析那一块,我们自己又创建了并发的多线程,去对这些数据进行了各种类型的解析。
    • 最后我们如果有自定义的completionQueue,则在自定义的queue中回调回来,也就是分线程回调回来,否则就是主队列,主线程中回调结束。
    AFNetWorking 2.x

    To be continue......

    面试题解惑
    为什么2x版本有一条常驻线程

    再清楚了2x和3x版本,再来看一开始的面试题,之所以在2x版本中有一条常驻的线程,其原因和2x版本用NSURLConnection来发起请求有关。在NSURLConnection发起请求的方法中有个参数startImmediately,苹果中对它的解释是这个值默认为YES,而且任务完成的结果会在主线程的runloop中回调(代理方法回调的线程)。如果我们设置为NO,我们需要自己去给他设置一个子线程的runloop回调。

    // NSURLConnection发起请求。
    self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
    
    

    那如果我们用NSURLConnection,我们为了获取请求结果有以下三种选择:

    • 在主线程调异步接口
    • 每一个请求用一个线程,对应一个runloop,然后等待结果回调。
    • 只用一条线程,一个runloop,所有结果回调在这个线程上。

    很显然AF选择的是第3种方式,创建了一条常驻线程专门处理所有请求的回调事件。
    那么选择第1中有什么缺点呢:

    • 如果我们放到主线程去做,那么一般会回到到主线程的runloop的defaultMode中,那么当出现类似tableView滚动的时候,在tracemodel下就会收不到请求。
      就算另外写代码放到commonMode下,试想如果有大量的网络请求,同时回调回来,就会影响我们的UI体验了。
    • 另外如果我们请求数据返回,势必要进行数据解析,解析成我们需要的格式,那么这些解析都在主线程中做,给主线程增加额外的负担。
      又或者我们回调回来开辟一个新的线程去做数据解析,那么我们有n个请求回来开辟n条线程带来的性能损耗,以及线程间切换带来的损耗,是不是一笔更大的开销。

    那选择2中方式呢?其实更傻,为了等待不确定的请求结果,阻塞住线程,白白浪费n条线程的开销。(子线程要一直等着回调。)
    综上所述,AF2x中有一条常驻线程。

    注:NSURLConnection的所有请求是在这个常驻线程中发起的,指定所有的代理回调到该线程中来处理。

    AFNetWorking3x版本是怎么做的

    在3x版本中,网络请求是通过NSURLSession来发起的。那么情况就和2x完全不一样了。

    dataTask = [self.session dataTaskWithRequest:request];
    
    

    我们来看看self.session 是怎么初始化的。

        self.operationQueue = [[NSOperationQueue alloc] init];
        self.operationQueue.maxConcurrentOperationCount = 1;
        self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
    
    • 从上面的代码可以看出,我们初始化session的时候,先初始化了NSOperationQueue,且设置了operationQueue.maxConcurrentOperationCount = 1;(这里很关键)把session的delegateQueue设置成了我们初始化的NSOperationQueue。那么意味着NSURLSession发起的多线程的网络请求的回调回来在我们一开始生成的并发数为1的NSOperationQueue中,这个时候会是多线程串行的回调回来的。这样和2x版本的常驻线程异曲同工之妙。
    • 另外因为跟代理相关的一些操作AF都使用了NSLock。所以就算Queue的并发数设置为n,因为多线程回调,锁的等待,导致所提升的程序速度也并不明显。反而多task回调导致的多线程并发,平白浪费了部分性能。(注:这里虽然回调Queue的并发数为1,当是NSURLSession发起的请求是多线程的,所以还是有多个线程存在的,但是因为是串行回调,所以同一时间,只会有一条线程在操作那些代理。)

    文章借鉴了很多涂耀辉大佬关于AF的心得

    相关文章

      网友评论

          本文标题:AFNetWorking浅析

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