AFNetworking源碼——設(shè)計(jì)思路

AFNetworking是一個(gè)非常簡潔的框架,關(guān)于基本架構(gòu),可以看看這篇文章,本文主要闡述AFNetworking在設(shè)計(jì)上是如何對(duì)NSURLSession封裝的。本文大致分為兩個(gè)部分,第一個(gè)部分為NSURLSession的設(shè)計(jì),第二個(gè)部分為AFNetworking的封裝設(shè)計(jì)

一、NSURLSession設(shè)計(jì)

NSURLSession主要由這幾個(gè)部分組成:

  • NSURLSession
  • NSURLSessionTask(擁有三種子類)
  • NSURLSessionConfiguration
  • 代理方法

首先我們通過一段Session的使用代碼來看各部分之間的關(guān)系:

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; // #1
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
                                                      delegate:self
                                                 delegateQueue:[NSOperationQueue mainQueue]]; // #2
NSURLSessionDataTask *task = [session dataTaskWithURL:[[NSURL alloc]initWithString:@""]]; // #3
[task resume];

為了方便理解這幾個(gè)部分之間的關(guān)系,這段代碼采用了delegate進(jìn)行回調(diào)處理

  • #1:創(chuàng)建了NSURLSessionConfiguration對(duì)象,該對(duì)象的工廠模式方法提供了三種對(duì)象
#if FOUNDATION_SWIFT_SDK_EPOCH_AT_LEAST(8)
@property (class, readonly, strong) NSURLSessionConfiguration *defaultSessionConfiguration;
@property (class, readonly, strong) NSURLSessionConfiguration *ephemeralSessionConfiguration;
#endif
 + (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier NS_AVAILABLE(10_10, 8_0);

以上有三種方法,下面簡要介紹下每種類型的特點(diǎn)

  • defaultSessionConfiguration:默認(rèn)的配置,和NSURLConnection的配置類似,使用硬盤來緩存數(shù)據(jù)(不同的是NSURLConnection的配置是全局的)
  • ephemeralSessionConfiguration:不會(huì)將Cookie、緩存等存儲(chǔ)到磁盤,而是放在內(nèi)存中,程序退出時(shí)數(shù)據(jù)會(huì)消失(可以用于私密瀏覽)
  • backgroundSessionConfigurationWithIdentifier:可以在應(yīng)用程序掛起、退出、崩潰的情況下運(yùn)行下載和上傳任務(wù),會(huì)在后臺(tái)另外開啟一個(gè)線程,但是系統(tǒng)會(huì)根據(jù)負(fù)載程度去調(diào)度這個(gè)線程的操作,可能會(huì)造成速度緩慢或者超時(shí)

三種工廠提供了三種對(duì)象具有不同的特點(diǎn),除此之外,NSURLSessionConfiguration擁有很多的屬性可以進(jìn)行配置



這里列出一些常用屬性:

  • @property NSTimeInterval timeoutIntervalForRequest;:請(qǐng)求超時(shí)
  • @property NSTimeInterval timeoutIntervalForResource;:資源超時(shí)
  • @property (nullable, copy) NSDictionary *HTTPAdditionalHeaders;:請(qǐng)求頭,配置如下
configuration.HTTPAdditionalHeaders = @{@"Accept": @"application/json",
                                            @"Accept-Language": @"en",
                                            @"Accept-Encoding": @"",
                                            @"Authorization": @"",
                                            @"Connection": @"",
                                            @"User-Agent": @""};

可以發(fā)現(xiàn)字典中的key都是標(biāo)準(zhǔn)的HTTP請(qǐng)求頭的key,可以通過這種方式對(duì)請(qǐng)求頭進(jìn)行自定義配置

  • #2:創(chuàng)建了NSURLSession對(duì)象,依賴于三個(gè)參數(shù)

    • NSURLSessionConfiguration:配置
    • Delegate:代理對(duì)象
    • DelegateQueue:代理隊(duì)列,在NSURLConnection中往往需要指定代理隊(duì)列,代表回調(diào)方法在哪個(gè)線程中執(zhí)行,NSURLSession提供了類初始化方法,省略了代理隊(duì)列的指定,默認(rèn)為主線程的主隊(duì)列

    除了這種方式之外,還可以通過單例模式提供的類方法創(chuàng)建,內(nèi)部實(shí)現(xiàn)可能沒有設(shè)置代理和代理隊(duì)列,采用的是默認(rèn)配置

@property (class, readonly, strong) NSURLSession *sharedSession;

這里的session使用了delegate回調(diào),蘋果還提供了block回調(diào)的形式:

NSURLSessionDataTask *task = [session dataTaskWithURL:[[NSURL alloc]initWithString:@""]
                                    completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
                                        
                                    }];
  • #3:創(chuàng)建NSURLSessionTask對(duì)象,NSURLSession提供了這幾種task類型

    • NSURLSessionTask:超類,一般不具體使用
    • NSURLSessionDataTask:請(qǐng)求
    • NSURLSessionUploadTask:上傳
    • NSURLSessionDownloadTask:下載

    它們之間的繼承關(guān)系如下:


總結(jié):NSURLSession的設(shè)計(jì)主要有三個(gè)部分,三個(gè)部分相互獨(dú)立又具有聯(lián)系,NSURLSessionConfiguration進(jìn)行配置管理。NSURLSession將配置,代理,代理隊(duì)列等對(duì)象關(guān)聯(lián),用于創(chuàng)建任務(wù)。NSURLSessionTask是任務(wù)類,其對(duì)象具有操作該任務(wù)的各種方法,啟動(dòng),暫停等,同時(shí)任務(wù)的回調(diào)提供兩種方式,block和代理。

二、AFNetworking的封裝設(shè)計(jì)

這里先回憶一下在基本架構(gòu)這篇文章中所提到的AFNetworking的使用代碼被分為兩個(gè)部分,第一個(gè)部分是初始化AFHTTPSessionManager對(duì)象,第二個(gè)部分是調(diào)用請(qǐng)求方法。
先來看第一個(gè)部分,我們?cè)俅位仡櫼幌路椒ǖ恼{(diào)用棧

- [AFHTTPSessionManager initWithBaseURL:]
    - [AFHTTPSessionManager initWithBaseURL:sessionConfiguration:] // #1
        - [AFURLSessionManager initWithSessionConfiguration:] // #2
            - [NSURLSession sessionWithConfiguration:delegate:delegateQueue:]
            - [AFJSONResponseSerializer serializer] 
            - [AFSecurityPolicy defaultPolicy] 
            - [AFNetworkReachabilityManager sharedManager] 
        - [AFHTTPRequestSerializer serializer] 
        - [AFJSONResponseSerializer serializer] 

我們順著方法調(diào)用棧,看方法具體實(shí)現(xiàn)細(xì)節(jié),從而理解AFNetworking是如何封裝NSURLSession的

  • #1
           sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
    self = [super initWithSessionConfiguration:configuration]; // &1
    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;  // &2
    self.requestSerializer = [AFHTTPRequestSerializer serializer];
    self.responseSerializer = [AFJSONResponseSerializer serializer];
    return self;
}

&1:這里調(diào)用了父類AFURLSessionManager的初始化方法
&2:設(shè)置了baseURL

  • #2
 - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super init];
    if (!self) {
        return nil;
    }
    if (!configuration) {
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; // &1
    }
    self.sessionConfiguration = configuration;
    // &2
    self.operationQueue = [[NSOperationQueue alloc] init];
    self.operationQueue.maxConcurrentOperationCount = 1;
    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue]; // &3
    // &4
    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;
    // &5
    [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:確定配置對(duì)象(原生調(diào)用)
&2:設(shè)置一個(gè)隊(duì)列,并設(shè)置為串行(最大并發(fā)為1),在&3中創(chuàng)建session的時(shí)候作為參數(shù)(由于AFNetworking將AFURLSessionManager類作為了NSURLSession的代理,所以這里另外添加一個(gè)在子線程中的操作隊(duì)列)
&3:創(chuàng)建NSURLSession對(duì)象(原生調(diào)用)
&4:設(shè)置AFNetworking中的序列化和安全策略(AF自己的模塊封裝)
&5:為每個(gè)task添加一個(gè)AF封裝的Delegate,后文會(huì)提到

  • 總結(jié)第一部分
    從上面的代碼細(xì)節(jié)來看,AFHTTPSessionManager的初始化操作就是做了這些事情:

    • 獲得了NSURLSessionConfiguration對(duì)象
    • 創(chuàng)建了NSURLSession對(duì)象,并設(shè)置自身為代理類,添加了操作隊(duì)列
    • 設(shè)置了AF自己封裝的序列化和安全策略

    這樣一來,就很容易看出,其實(shí)就是在原生的初始化操作上添加了一些AF自己封裝的策略對(duì)象

接下來再看看第二部分,同樣回顧一下調(diào)用棧
使用GET:parameters:process:success:failure:方法作為例子來查看一下源碼實(shí)現(xiàn)

- [AFHTTPSessionManager GET:parameters:process:success:failure:] // #1
    - [AFHTTPSessionManager dataTaskWithHTTPMethod:parameters:uploadProgress:downloadProgress:success:failure:] // #2
        - [AFHTTPRequestSerializer requestWithMethod:URLString:parameters:error:] 
        - [AFURLSessionManager dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:]  // #3
            - [NSURLSession dataTaskWithRequest:]
            - [AFURLSessionManager addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler:] // #4
                - [AFURLSessionManagerTaskDelegate init]
                - [AFURLSessionManager setDelegate:forTask:] // #5
    - [NSURLSessionDataTask resume]
  • #1
- (NSURLSessionDataTask *)GET:(NSString *)URLString
                   parameters:(id)parameters
                     progress:(void (^)(NSProgress * _Nonnull))downloadProgress
                      success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
                      failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{

    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                        URLString:URLString
                                                       parameters:parameters
                                                   uploadProgress:nil
                                                 downloadProgress:downloadProgress
                                                          success:success
                                                          failure:failure];

    [dataTask resume];

    return dataTask;
}

從表層能夠看出返回了一個(gè)NSURLSessionDataTask對(duì)象,并且調(diào)用了resume操作(和原生一樣),我們接著看下返回對(duì)象的方法是如何實(shí)現(xiàn)的

  • #2
 - (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]; // &1
    if (serializationError) {
        if (failure) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
#pragma clang diagnostic pop
        }

        return nil;
    }
    // &2
    __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:創(chuàng)建了NSURLRequest,可以發(fā)現(xiàn)AF內(nèi)部是通過Request的方式創(chuàng)建的task,而不是URL
&2:調(diào)用另外一個(gè)方法返回task對(duì)象

  • #3
  - (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(^{
        dataTask = [self.session dataTaskWithRequest:request]; // &1
    });

    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler]; // &2

    return dataTask;
}

&1:這部分就很熟悉了,利用原生的方式通過request來創(chuàng)建task對(duì)象,然后返回。到這里,就已經(jīng)能明白關(guān)于task對(duì)象是如何被封裝返回的
&2:按照名字來看,這里好像是為task對(duì)象添加代理方法的,我們繼續(xù)往下研究

  • #4
  - (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
{
    // &1
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:dataTask]; // &2
    // &3
    delegate.uploadProgressBlock = uploadProgressBlock;
    delegate.downloadProgressBlock = downloadProgressBlock;
}

&1:新建了一個(gè)AFURLSessionManagerTaskDelegate對(duì)象,是AF自己封裝的代理對(duì)象
&2:看樣子還需要進(jìn)一步查看設(shè)置代理的細(xì)節(jié),這一個(gè)方法傳入了代理對(duì)象和task對(duì)象,我們繼續(xù)看
&3:將block賦值給delegate的block屬性,方便回調(diào)

  • #5
  - (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    NSParameterAssert(task);
    NSParameterAssert(delegate);

    [self.lock lock]; // &1
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate; // &2
    [delegate setupProgressForTask:task];
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

&1:有個(gè)加鎖操作,保證線程安全
&2:用task的taskIdentifiier屬性為key,delegate為value來進(jìn)行對(duì)應(yīng),到這里,可以知道AF是用字典將delegate和task一一對(duì)應(yīng)的

  • 總結(jié)第二部分
    通過上面的分析,可以看出第二部分實(shí)際上就是利用NSURLRequest去創(chuàng)建NSURLSessionTask對(duì)象。同時(shí)呢,AFURLSessionManager作為了NSURLSession的代理,AF內(nèi)部自定義了一個(gè)AFURLSessionManagerTaskDelegate代理類,該類具有很多block屬性。并且,AF在內(nèi)部實(shí)現(xiàn)了NSURLSession的代理方法,方法實(shí)現(xiàn)中實(shí)現(xiàn)block賦值,代理類的block屬性對(duì)外暴露,在合適的地方回調(diào)。AF將很多NSRULSession中的代理方法都變成block形式進(jìn)行暴露,更加簡潔。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容