iOS網絡請求-AFNetworking源碼解析

趁着端午節日,本身沒有什麼過多的安排,準備花4-5天左右,針對網絡請求源碼AFNetworking和YTKNetwork進行解析以及這兩年多iOS實際開發經驗(其實YTKNetwork也是對AFNetworking的深度封裝),結合多個實際項目,分別針對這兩個網絡框架,進行封裝使用(能夠直接使用)。本篇主要講解AFNetworking源碼解析,都是本身親自看AFNetworking源碼以及心得體會,大約看下來須要20-30分鐘。歡迎指正!!!html

 AFNetworking源碼地址:https://github.com/AFNetworking/AFNetworkinggit

針對AFNetworking封裝:http://www.javashuo.com/article/p-tpkndbbq-v.htmlgithub

YTKNetwork的源碼詳解:http://www.javashuo.com/article/p-spkgoewu-bv.html緩存

 

一.AFNetworking的代碼結構:安全

新的代碼結構將AFNetworking.h放到了Supporting Files裏面。服務器

自從AFNetworking結構更改之後,結構可能不夠清晰,之前的版本是這樣的:網絡

其實沒有多少改變,從這張圖能夠看出:除去Support Files,能夠看到AF分爲以下5個功能模塊:session

  • 網絡通訊模塊(最核心)(AFURLSessionManager、AFHTTPSessionManager)
  • 網絡狀態監聽模塊(Reachability)
  • 網絡通訊安全策略模塊(Security)
  • 網絡通訊信息序列化/反序列化模塊(Serialization)
  • 對於iOS UIkit庫的拓展(UIKit)

 二.各種講解app

1.網絡通訊模塊-AFURLSessionManager與AFHTTPSessionManager框架

AFHTTPSessionManager是繼承AFURLSessionManager的,至關於對AFURLSessionManager的再次封裝。

(1)AFURLSessionManager

1>經過查看AFURLSessionManager類:

發現AFURLSessionManager遵照NSSecureCoding, NSCopying兩個協議,以及遵照

NSURLSessionDelegate,NSURLSessionTaskDelegate,NSURLSessionDataDelegate,NSURLSessionDownloadDelegate四個代理。在AFURLSessionManager中實現協議裏的方法,用來處理網絡請求中不一樣的狀況:例如:暫停,取消,數據保存,更新數據進度條同樣。

2>下面是查看AFURLSessionManager類,所包含的屬性:

3>下面是AFURLSessionManager的方法

(1)

若是入參configuration爲nil,則調用NSURLSessionConfiguration的defaultSessionConfiguration方法,建立一個會話配置,並使用該配置建立一個會話對象,同時還初始化了安全策略、鎖、返回數據解析器(JSON 數據解析器)等屬性。

(2)

此方法是取消會話Session,發現cancelPendingTasks是一個BOOL類型,若是返回NO,意思是容許會話中的任務執行完畢後,再取消會話,可是會話一經取消將沒法重啓;反之若是返回YES,那麼直接取消會話,其相關聯的任務和回調都將會釋放。

(3)

再看一下實現方法:

- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                            completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    return [self dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:completionHandler];
}

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

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

    return dataTask;
}
 

建立數據服務,這裏在使用會話對象 session 和入參 request 建立任務時,若是 NSFoundationVersionNumber 的值小於 NSFoundationVersionNumber_iOS_8_0 那麼 dataTask 的建立會放在 af_url_session_manager_creation_queue 串行隊列中同步執行,不然就由當前線程執行。接着,會調用這樣的方法:

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

}

在這個方法中會建立一個AFURLSessionManagerTaskDelegate對象,設置其相關聯的管理器,任務描述以及回調等甦醒,還會將當前會話註冊爲監聽者,監聽 task 任務發出的 AFNSURLSessionTaskDidResumeNotification 和 AFNSURLSessionTaskDidSuspendNotification 通知。當接收到該通知後,分別執行 taskDidResume: 和 taskDidSuspend: 方法,在這兩個方法中又發出了 AFNetworkingTaskDidResumeNotification 和 AFNetworkingTaskDidSuspendNotification 通知。

(4)

- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                            completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    return [self dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:completionHandler];
}

建立數據服務,這個方法實際上是調用了上一個方法,只是uploadProgress和downloadProgress傳nil而已

(5)

- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
                                         fromFile:(NSURL *)fileURL
                                         progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    __block NSURLSessionUploadTask *uploadTask = nil;
    url_session_manager_create_task_safely(^{
        uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
    });
    if (!uploadTask && self.attemptsToRecreateUploadTasksForBackgroundSessions && self.session.configuration.identifier) {
        for (NSUInteger attempts = 0; !uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) {
            uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
        }
    }

    [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];

    return uploadTask;
}

此方法是建立文件上傳任務,若是果後臺會話對象建立文件上傳任務失敗時,會根據條件嘗試從新建立,固然 AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask 爲 5 ,因此只能嘗試 5次。若是任務建立成功,則進而爲任務建立一個 AFURLSessionManagerTaskDelegate 對象,做爲任務的代理。請求報文的請求體數據即爲根據參數 fileURL 獲取的文件數據。

(6)

- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
                                         fromData:(NSData *)bodyData
                                         progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    __block NSURLSessionUploadTask *uploadTask = nil;
    url_session_manager_create_task_safely(^{
        uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];
    });

    [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];

    return uploadTask;
}

此方法上傳數據與上面上傳文件相似,待上傳的數據直接由參數 bodyData 給出。

(7)

- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
                                             progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                                          destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                    completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
    __block NSURLSessionDownloadTask *downloadTask = nil;
    url_session_manager_create_task_safely(^{
        downloadTask = [self.session downloadTaskWithRequest:request];
    });

    [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];

    return downloadTask;
}

建立下載任務:

  • request 建立任務時使用的請求報文頭信息
  • downloadProgressBlock 下載進度更新時調用的代碼塊,這個代碼會在會話隊列中調用,因此若是更新視圖,須要本身在任務代碼中指定主隊列
  • destination 任務下載結束後,該參數能夠返回指定的文件保存地址,緩存數據被移動到該地址,targetPath 爲下載的數據緩存地址
  • completionHandler 下載任務結束後的回調

進一步調用下面方法

- (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask
                          progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                       destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                 completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

    if (destination) {
        delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
            return destination(location, task.response);
        };
    }

    downloadTask.taskDescription = self.taskDescriptionForSessionTasks;

    [self setDelegate:delegate forTask:downloadTask];

    delegate.downloadProgressBlock = downloadProgressBlock;
}

(8)

- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
                                                progress:(NSProgress * __autoreleasing *)progress
                                             destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                       completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
    __block NSURLSessionDownloadTask *downloadTask = nil;
    dispatch_sync(url_session_manager_creation_queue(), ^{
        downloadTask = [self.session downloadTaskWithResumeData:resumeData];
    });

    [self addDelegateForDownloadTask:downloadTask progress:progress destination:destination completionHandler:completionHandler];

    return downloadTask;
}

建立重用數據的下載任務:使用已經下載的部分數據 resumeData 建立一個下載任務,繼續進行下載。

(9)

- (nullable NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task;

獲取任務的數據上傳進度

(10)

- (nullable NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task;

獲取任務的數據下載進度

 AFURLSessionManager 中實現的代理方法

AFURLSessionManager 遵循 NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate 協議,以處理網絡請求過程當中的數據。

有些代理方法中所作的任務,徹底由 AFURLSessionManager 的代碼塊屬性決定。若是這些屬性並無設置,那麼相應的代理方法就不必響應。因此 AFURLSessionManager 中重寫了 respondsToSelector: 過濾了一些沒必要響應的代理方法。

NSURLSessionDownloadDelegate

當下載任務結束後,調用該代理方法。

- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
    //獲取與任務相對應的代理對象
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];

    //若是設置了回調任務,先執行回調任務
    if (self.downloadTaskDidFinishDownloading) {

        //獲取下載數據要保存的地址        
        NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
        if (fileURL) {
            delegate.downloadFileURL = fileURL;
            NSError *error = nil;

            //移動下載的數據到指定地址
            if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) {
                [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
            }

            return;
        }
    }

    if (delegate) {
        [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
    }
}

從上面的代碼可知,若是會話管理器的 downloadTaskDidFinishDownloading 的代碼塊返回了地址,那麼便不會去執行任務自己所對應的代理方法了,而且若是移動文件失敗便會推送一個 AFURLSessionDownloadTaskDidFailToMoveFileNotification 通知。

下面兩個協議方法中,都是先執行任務所關聯的代理對象的方法,再執行會話對象設置的 downloadTaskDidWriteData 或 downloadTaskDidResume 任務。

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

從這些代理方法中可知,設置 AFURLSessionManager 會話實例對象的代碼塊任務屬性,那麼這些回調任務對於每個網絡請求任務都是有效的,因此針對於單個特殊的任務回調操做,便不能放在會話管理器的屬性中,而是要放在與任務相關聯的 AFURLSessionManagerTaskDelegate 代理對象中。

實際使用 AFURLSessionManager 的方法建立網絡請求任務時,傳遞的回調任務,都是在與任務相關聯的代理對象的方法中執行的。

以上就是AFURLSessionManager的內容。下面講解他的子類:AFHTTPSessionManager

2>AFHTTPSessionManager

發現AFHTTPSessionManager是繼承AFURLSessionManager並遵照<NSSecureCoding, NSCopying>

(1)

/**
 The URL used to construct requests from relative paths in methods like `requestWithMethod:URLString:parameters:`, and the `GET` / `POST` / et al. convenience methods.
 */
@property (readonly, nonatomic, strong, nullable) NSURL *baseURL;

/**
 Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `AFHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies.

 @warning `requestSerializer` must not be `nil`.
 */
@property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;

/**
 Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`.

 @warning `responseSerializer` must not be `nil`.
 */
@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;
/**
 The security policy used by created session to evaluate server trust for secure connections. `AFURLSessionManager` uses the `defaultPolicy` unless otherwise specified. A security policy configured with `AFSSLPinningModePublicKey` or `AFSSLPinningModeCertificate` can only be applied on a session manager initialized with a secure base URL (i.e. https). Applying a security policy with pinning enabled on an insecure session manager throws an `Invalid Security Policy` exception.
 */
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;

暴露出的 baseUrl 屬性是 readonly 
@property (readonly, nonatomic, strong, nullable) NSURL *baseURL;

// 請求序列化 
@property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;

// 響應序列化 
@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;

以及

+ (instancetype)manager; 
- (instancetype)initWithBaseURL:(nullable NSURL *)url; 
- (instancetype)initWithBaseURL:(nullable NSURL *)url 
sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration

三個初始化方法,第一個是類方法,後兩個是須要傳參的實例方法。

(2)請求方式

GET 請求是向服務端發起請求數據,用來獲取或查詢資源信息 
POST 請求是向服務端發送數據的,用來更新資源信息,它能夠改變數據的種類等資源 
PUT 請求和POST請求很像,都是發送數據的,可是PUT請求不能改變數據的種類等資源,它只能修改內容 
DELETE 請求就是用來刪除某個資源的 
PATCH 請求和PUT請求同樣,也是用來進行數據更新的,它是HTTP verb推薦用於更新的 
在實際開發過程當中,咱們仍是使用【GET 和 POST】請求是最多的。

其實若是看一下AFHTTPSessionManager,發現裏面並無須要講什麼的,關鍵仍是AFURLSessionManager類。

下面以GET請求爲例:

//GET請求,調用下面那個方法
- (NSURLSessionDataTask *)GET:(NSString *)URLString
                   parameters:(id)parameters
                      success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                      failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{

    return [self GET:URLString parameters:parameters progress:nil success:success failure:failure];
}

//GET請求
- (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
{
    //調用另外一個方法構造GET請求
    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                        URLString:URLString
                                                       parameters:parameters
                                                   uploadProgress:nil
                                                 downloadProgress:downloadProgress
                                                          success:success
                                                          failure:failure];
    
    /*
    啓動任務
    使用AFHTTPSessionManager建立的任務默認都幫你啓動了,因此不須要手動調用resume方法了
    上一篇中講解的AFURLSessionManager默認沒有啓動,因此獲取任務後要手動啓動
    */
    [dataTask resume];

    return dataTask;
}
- (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;
//利用AFURLSessionManager方法
    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;
}

2.AFNetworkReachabilityManager

 AFNetworkReachabilityManager對象用於監聽設備當前鏈接網絡的狀態。

AFNetworkReachabilityManager提供了4種建立方法:

/**
 Returns the shared network reachability manager.
 */
+ (instancetype)sharedManager;

/**
 Creates and returns a network reachability manager with the default socket address.
 
 @return An initialized network reachability manager, actively monitoring the default socket address.
 */
+ (instancetype)manager;

/**
 Creates and returns a network reachability manager for the specified domain.

 @param domain The domain used to evaluate network reachability.

 @return An initialized network reachability manager, actively monitoring the specified domain.
 */
+ (instancetype)managerForDomain:(NSString *)domain;

/**
 Creates and returns a network reachability manager for the socket address.

 @param address The socket address (`sockaddr_in6`) used to evaluate network reachability.

 @return An initialized network reachability manager, actively monitoring the specified socket address.
 */
+ (instancetype)managerForAddress:(const void *)address;
+ ( instancetype)sharedManager; //建立單例對象
+ (instancetype)manager; //建立實例對象
+ (instancetype)managerForDomain:(NSString*)domain; //根據地址名建立實例對象
+ (instancetype)managerForAddress:(constvoid*)address; //根據sockaddr建立實例對象

(1)+ ( instancetype)sharedManager; //建立單例對象

代碼以下:建立單例對象:
+ (instancetype)shareManager{
    static AFNetworkReachabilityManager *_shareManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken,^{
       _shareManager = [self manager];
)  ;
return _shareManager;
}    

sharedManager調用manager方法建立一個AFNetworkReachabilityManager對象,代碼以下:

//其中sockaddr_in6和sockaddr_in是描述網絡套接字的結構體,包含協議族類型、端口、ip地址等信息,而後調用managerForAddress:方法建立,
+ (instancetype)manager
{
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
    struct sockaddr_in6 address;
    bzero(&address, sizeof(address));
    address.sin6_len = sizeof(address);
    address.sin6_family = AF_INET6;
#else
    struct sockaddr_in address;
    bzero(&address, sizeof(address));
    address.sin_len = sizeof(address);
    address.sin_family = AF_INET;
#endif
    return [self managerForAddress:&address];
}

其餘的兩種方法: 一個根據地址名建立實例對象,另外一個根據sockaddr建立實例對象,能夠直接看源代碼,用的並非特別多。

(2)經過枚舉值查看網絡狀態

typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
    AFNetworkReachabilityStatusUnknown          = -1,//未知狀態
    AFNetworkReachabilityStatusNotReachable     = 0, //未鏈接
    AFNetworkReachabilityStatusReachableViaWWAN = 1, //蜂窩移動網絡(2G/3G/4G)
    AFNetworkReachabilityStatusReachableViaWiFi = 2, //wifi網絡
};

(3)網絡監聽

AFNetworkReachabilityManager經過startMonitoring方法和stopMonitoring開始並中止監聽當前設備鏈接的網絡狀態。

1>startMonitoring方法

該方法主要經過SystemConfiguration框架提供的API將networkReachability讓對象加入runloop中,開始工做,而且綁定監聽的回調函數處理狀態改變。

- (void)startMonitoring {
    [self stopMonitoring];

    if (!self.networkReachability) {
        return;
    }

    __weak __typeof(self)weakSelf = self;
    AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
        __strong __typeof(weakSelf)strongSelf = weakSelf;

        strongSelf.networkReachabilityStatus = status;
        if (strongSelf.networkReachabilityStatusBlock) {
            strongSelf.networkReachabilityStatusBlock(status);
        }

    };

    SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
    SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
    SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
        SCNetworkReachabilityFlags flags;
        if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
            AFPostReachabilityStatusChange(flags, callback);
        }
    });
}

根據查看源碼發現:該方法首先中止以前的監聽,而後調用SCNetworkReachabilitySetCallback方法來設置networkReachability的回調函數AFNetworkReachabilityCallback和上下文context對象。

在回調方法中有一個方法比較重要:

static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) {
    AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); //根據flags獲取當前網絡鏈接狀態status
    dispatch_async(dispatch_get_main_queue(), ^{
        if (block) {
            block(status); //block是context中的info指針,調用info將status傳遞給外界
        }
         //status做爲通知的值,發通知拋給外界
        NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
        NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) };
        [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo];
    });
}

此方法首先根據系統回調的AFNetworkReachabilityStatusForFlags方法以及falgs參數,獲取網絡鏈接狀態,而後進入block,將status拋出外界,同時拋一個通知將status拋給外界,當網絡狀態發生改變,會同事用這兩種方式傳遞給外界。

AFNetworkReachabilityStatusForFlags方法是核心方法,負責根據flag的狀態值,轉化爲相應的枚舉值AFNetworkReachabilityStatus。

2>stopMonitoring方法

該方法經過監聽的方法是讓networkReachability對象從runloop中註銷。

- (void)stopMonitoring {
    if (!self.networkReachability) {
        return;
    }

    SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}

(3)網絡屬性狀態

/**
 The current network reachability status.
 */
@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;

/**
 Whether or not the network is currently reachable.
 */
@property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable;

/**
 Whether or not the network is currently reachable via WWAN.
 */
@property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN;

/**
 Whether or not the network is currently reachable via WiFi.
 */
@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi;

經過上面可發現:外界可經過isReachable方法,isReachableViaWWAN方法以及isReachableViaWiFi等獲取當前的網絡狀態。經過keyPathsForValuesAffectingValueForKey方法設置屬性值的依賴關係,利用KVO方式監聽屬性的變化。

下面是簡單的一個網絡監聽的小demo

1)[[AFNetworkReachabilityManagersharedManager] startMonitoring];

2)判斷網絡鏈接狀態。

 [[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {  
          
        switch (status) {  
                  
            case AFNetworkReachabilityStatusNotReachable:{  
                  
                NSLog(@"無網絡");  
                  
                break;  
                  
            }  
                  
            case AFNetworkReachabilityStatusReachableViaWiFi:{  
                  
                NSLog(@"WiFi網絡");  
                  
                break;  
                  
            }  
                  
            case AFNetworkReachabilityStatusReachableViaWWAN:{  
                  
                NSLog(@"3G網絡");  
                  
                break;  
                  
            }  
                  
            default:  
                  
                break;  
                  
        }  
          
    }];  

3.AFSecurityPolicy

首先看一下AFSecurityPolicy暴露出來的方法。

//https驗證模式 默認是無,還有證書匹配和公鑰匹配
@property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode;
//能夠去匹配服務端證書驗證的證書 當咱們不主動設置pinningMode的時候不會主動該字段是空的,若是主動設置,會調用默認讀取certificatesInBundle .cer的證書進行賦值 源碼裏面有
@property (nonatomic, strong, nullable) NSSet <NSData *> *pinnedCertificates;
//allowInvalidCertificates 是否容許無效證書(也就是自建的證書),默認爲NO
//若是是須要驗證自建證書,須要設置爲YES 通常測試的時候YES,https開啓就弄爲NO
@property (nonatomic, assign) BOOL allowInvalidCertificates;
//validatesDomainName 是否須要驗證域名,默認爲YES;
//假如證書的域名與你請求的域名不一致,需把該項設置爲NO;如設成NO的話,即服務器使用其餘可信任機構頒發的證書,也能夠創建鏈接,這個很是危險,建議打開。
//置爲NO,主要用於這種狀況:客戶端請求的是子域名,而證書上的是另一個域名。由於SSL證書上的域名是獨立的,假如證書上註冊的域名是www.google.com,那麼mail.google.com是沒法驗證經過的;固然,有錢能夠註冊通配符的域名*.google.com,但這個仍是比較貴的。
//如置爲NO,建議本身添加對應域名的校驗邏輯。
@property (nonatomic, assign) BOOL validatesDomainName;

AFNetworking認證核心代碼

- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
//挑戰處理類型爲 默認
    /*
     NSURLSessionAuthChallengePerformDefaultHandling:默認方式處理
     NSURLSessionAuthChallengeUseCredential:使用指定的證書
     NSURLSessionAuthChallengeCancelAuthenticationChallenge:取消挑戰
     */
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    // 服務器挑戰的證書
    __block NSURLCredential *credential = nil;
// 這個Block是提供給用戶自定義證書挑戰方式的,好比是否須要自定義
    if (self.sessionDidReceiveAuthenticationChallenge) {
        disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
    } else {
    // NSURLAuthenticationMethodServerTrust 單向認證關係 也就是說服務器端須要客戶端返回一個根據認證挑戰的保護空間提供的信任產生的挑戰證書
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        // 基於客戶端的安全策略來決定是否信任該服務器,不信任的話,也就不必響應挑戰
            if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
             // 建立挑戰證書(注:挑戰方式爲UseCredential和PerformDefaultHandling都須要新建挑戰證書)
                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                if (credential) {
                    disposition = NSURLSessionAuthChallengeUseCredential;
                } else {
                    disposition = NSURLSessionAuthChallengePerformDefaultHandling;
                }
            } else {
                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
            }
        } else {
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }
    }
// 完成挑戰
    if (completionHandler) {
        completionHandler(disposition, credential);
    }
}

4.AFURLRequestSerialization

AFURLRequestSerialization涉及兩個模塊:AFURLRequestSerialization和AFURLResponseSerialization

前者主要是修改請求頭部,提供了接口設置HTTP頭部字段,後者是處理響應的模塊,將請求放回的數據解析成相對應的格式。
5.UIKit+AFNetworking
若是須要使用 AFNetworking 的 UIKit 擴展時可直接在 Prefix.pch 文件中引入,或者在工程的相關文件中引入。
(1)AFAutoPurgingImageCache:用於緩存圖片的類,經過identifier來添加和搜索UIImage。
 
協議中添加圖片
- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier;

協議中刪除圖片

- (BOOL)removeImageWithIdentifier:(NSString *)identifier;

協議中刪除全部圖片

- (BOOL)removeAllImages;

經過identifier獲取圖片的方法:

- (nullable UIImage *)imageWithIdentifier:(NSString *)identifier;

(2)UIButton+AFNetworking

UIButton跟圖片相關的屬性大概有兩個,ImageBackgroundImage.因此這個分類就是賦予他們異步加載圖片的能力。

核心方法有如下:

- (void)setImageForState:(UIControlState)state
                 withURL:(NSURL *)url;
- (void)setImageForState:(UIControlState)state
                 withURL:(NSURL *)url
        placeholderImage:(nullable UIImage *)placeholderImage;
- (void)setImageForState:(UIControlState)state
          withURLRequest:(NSURLRequest *)urlRequest
        placeholderImage:(nullable UIImage *)placeholderImage
                 success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success
                 failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure;


- (void)setBackgroundImageForState:(UIControlState)state
                           withURL:(NSURL *)url;
- (void)setBackgroundImageForState:(UIControlState)state
                           withURL:(NSURL *)url
                  placeholderImage:(nullable UIImage *)placeholderImage;
- (void)setBackgroundImageForState:(UIControlState)state
                    withURLRequest:(NSURLRequest *)urlRequest
                  placeholderImage:(nullable UIImage *)placeholderImage
                           success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success
                           failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure;

(3)UIImageView+AFNetworking

UIImageView+AFNetworking 是AFNetworking中一個實現圖片異步加載的類, 它是爲系統中的UIImageView類添加的類目(Category), 這個類目中的方法爲遠程異步加載圖片功能提供支持.

思路:

  1. 導入AFNetworking工程文件.
  2. 引入UIKit+AFNetworking/UIImageView+AFNetworking.h頭文件.
  3. 下載圖片
  4. 取消圖片下載(可選)

實現異步加載:

/* 建立NSURL對象 */
    NSURL *urlStr = [NSURL URLWithString:@"http://img2.cache.netease.com/3g/2015/9/18/20150918195439dc844.jpg"];

/* 建立請求對象 */
    NSURLRequest *request = [NSURLRequest requestWithURL:urlStr];

/* 建立一個imageView對象 */
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];

方法一:

/** 
 * 方法一:
 * 直接使用url字符串獲取照片
 * @param urlStr : NSURL對象
 */
[imageView setImageWithURL:urlStr];

方法二:

/**
 * 方法二:
 * 直接使用URL(例如:http://img2.cache.netease.com/3g/2015/9/18/20150918195439dc844.jpg)異步加載圖片, 照片出現以前添加一個佔位的背景照片
 * @param urlStr : NSURL對象
 * @param placeholderImage : UIImage圖片對象
 */
[imageView setImageWithURL:urlStr placeholderImage:[UIImage imageNamed:@"discuss"]];

方法三:

/**
  * 方法三:
  * 使用URLRequest對象獲取照片, 照片在請求後的block塊中返回一個UIImage參數用於賦值或其餘做.
  * 參數中得兩個block塊:分別在請求成功時執行 success:^{},   
                           請求失敗時執行 failure:^{}。
  * @param request : NSURLRequest請求對象
  * @param placeholderImage : UIImage圖片對象
  */
 [imageView setImageWithURLRequest:request placeholderImage:[UIImage imageNamed:@"discuss"] success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
        /**
         * 返回值參數
         * @param request : NSURLRequest 請求對象
         * @param response : NSHTTPURLResponse HTTP響應者對象(包含請求對象和圖片信息)
         * @param image : 加載成功圖片UIImage對象(自帶的size尺寸用於自適應高度計算)
         */
        [imageView setImage:image];
    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
        /**< @param error : NSError對象 加載錯誤的緣由代號(例如 : 403) */
    }];
 /**
     * 取消圖片請求操做
     * 能夠直接取消本段代碼塊中在調用這個方法以前的全部異步加載操做(適當的地方慎重使用)
     */
    [imageView cancelImageRequestOperation];

固然還有其餘一些的擴展協議,但使用並非不少,且看源碼不是很難。上面就是這兩天看AFNetworking源碼的一些本身感悟:由於AFNetworking主要是AFURLSessionManager、AFHTTPSessionManager,本身花7成都在上面,但願對你們對AFNetworking的理解有所加深,歡迎指正。

 

下一篇,我將結合本身的項目對AFNetworking的使用進行封裝,對中小型企業APP的網絡封裝能夠直接使用,謝謝你們!!!

相關文章
相關標籤/搜索