源码分析之AFNetworking ①AFURLSessionManager与AFHTTPSessionManager

Posted 梦想家-mxj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了源码分析之AFNetworking ①AFURLSessionManager与AFHTTPSessionManager相关的知识,希望对你有一定的参考价值。

首先查看afn的结构,如下:

可以看到AF分为如下5个功能模块:

  •   网络通信模块(最核心)(AFURLSessionManager、AFHTTPSessionManager)
    
  •   网络状态监听模块(Reachability)
    
  •   网络通信安全策略模块(Security)
    
  •   网络通信信息序列化/反序列化模块(Serialization)
    
  •   对于ios UIkit库的拓展(UIKit)
    

一、1.网络通信模块-AFURLSessionManager与AFHTTPSessionManager
AFHTTPSessionManager是继承AFURLSessionManager的,相当于对AFURLSessionManager的再次封装。
(1)AFURLSessionManager
1>通过查看AFURLSessionManager类:

发现AFURLSessionManager遵守NSSecureCoding, NSCopying两个协议,以及遵守
NSURLSessionDelegate,NSURLSessionTaskDelegate,NSURLSessionDataDelegate,NSURLSessionDownloadDelegate四个代理。在AFURLSessionManager中实现协议里的方法,用来处理网络请求中不同的情况:例如:暂停,取消,数据保存,更新数据进度条一样。

2>下面是查看AFURLSessionManager类,所包含的属性:

3>下面是AFURLSessionManager的方法

如果入参configuration为nil,则调用NSURLSessionConfiguration的defaultSessionConfiguration方法,创建一个会话配置,并使用该配置创建一个会话对象,同时还初始化了安全策略、锁、返回数据解析器(JSON 数据解析器)等属性。


此方法是取消会话Session,发现cancelPendingTasks是一个BOOL类型,如果返回NO,意思是允许会话中的任务执行完毕后,再取消会话,但是会话一经取消将无法重启;反之如果返回YES,那么直接取消会话,其相关联的任务和回调都将会释放。


再看一下实现方法:

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

    NSURLSessionDataTask *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;

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

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

在这个方法中会创建一个AFURLSessionManagerTaskDelegate对象,设置其相关联的管理器,任务描述以及回调等苏醒,还会将当前会话注册为监听者,监听 task 任务发出的 AFNSURLSessionTaskDidResumeNotification 和 AFNSURLSessionTaskDidSuspendNotification 通知。当接收到该通知后,分别执行 taskDidResume: 和 taskDidSuspend: 方法,在这两个方法中又发出了 AFNetworkingTaskDidResumeNotification 和 AFNetworkingTaskDidSuspendNotification 通知。

④上传
使用NSURLSessionUploadTask进行文件上传,有三种方式:
NSData对象上传

-(NSURLSessionUploadTask )uploadTaskWithRequest:(NSURLRequest )request fromData:(NSData *)bodyData;

文件上传

-(NSURLSessionUploadTask )uploadTaskWithRequest:(NSURLRequest )request fromFile:(NSURL *)fileURL;

流上传

-(NSURLSessionUploadTask )uploadTaskWithStreamedRequest:(NSURLRequest )request;


此方法是创建文件上传任务

- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
                                         fromFile:(NSURL *)fileURL
                                         progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler

    NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
    
    if (uploadTask) 
        [self addDelegateForUploadTask:uploadTask
                              progress:uploadProgressBlock
                     completionHandler:completionHandler];
    

    return uploadTask;

如果果后台会话对象创建文件上传任务失败时,会根据条件尝试重新创建,当然 AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask 为 5 ,所以只能尝试 5次。如果任务创建成功,则进而为任务创建一个 AFURLSessionManagerTaskDelegate 对象,作为任务的代理。请求报文的请求体数据即为根据参数 fileURL 获取的文件数据。

- (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 给出。

数据流上传,实现

- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
                                                 progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                        completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler

    NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithStreamedRequest:request];

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

    return uploadTask;

例子:

NSURL *textFileURL = [NSURL fileURLWithPath:@"/path/to/file.txt"];
NSURL *url = [NSURL URLWithString:@"https://www.example.com/"];
NSMutableURLRequest *mutableRequest = [NSMutableURLRequest requestWithURL:url];
mutableRequest.HTTPMethod = @"POST";
mutableRequest.HTTPBodyStream = [NSInputStream inputStreamWithFileAtPath:textFileURL.path];
[mutableRequest setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];
[mutableRequest setValue:[NSString stringWithFormat:@"%lld", data.length] forHTTPHeaderField:@"Content-Length"];

NSURLSessionUploadTask *uploadTask = [defaultSession uploadTaskWithStreamedRequest:mutableRequest];
[uploadTask resume];

上传任务可以分三种方式上传,如果你的文件小,不怎么占内存,可以使用NSData创建上传任务,否则请使用文件创建上传任务。如果想边生产数据边上传的话,请使用流。

⑤下载:

实现:

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

    NSURLSessionDownloadTask *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;

另一个下载api

- (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创建一个下载任务,继续进行下载。
可用于断点下载

  • (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;
    这个方法可以讲当前的task已下载的数据使用一个data保存起来, 然后使用NSURLSession的方法可以根据这个data从服务器获取后续的数据
    例子:
+ (NSURLSessionDownloadTask *)downloadFileWithUrl:(NSString *)url DownloadProgress:(DownloadProgress)progress DownloadCompletion:(CompletionState)completion
    // 1、 设置请求
    NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
    // 2、初始化
    NSURLSessionConfiguration * configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    AFURLSessionManager * manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
    // 3、开始下载
    NSURLSessionDownloadTask * task = [manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) 
        progress(1.0 * downloadProgress.completedUnitCount / downloadProgress.totalUnitCount,1.0 * downloadProgress.totalUnitCount,1.0 * downloadProgress.completedUnitCount);
     destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) 
        //这里要返回一个NSURL,其实就是文件的位置路径
        NSString * path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        //使用建议的路径
        path = [path stringByAppendingPathComponent:response.suggestedFilename];
        NSLog(@"%@",path);
        return [NSURL fileURLWithPath:path];//转化为文件路径
     completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) 
        //如果下载的是压缩包的话,可以在这里进行解压
          NSLog(@"----%@---%d---%@",error.domain,error.code,error);
        //下载成功
        if (error == nil) 
            completion(YES,@"下载完成",[filePath path]);
        else//下载失败的时候,只列举判断了两种错误状态码
            NSString * message = nil;
            if (error.code == - 1005) 
                message = @"网络异常";
            else if (error.code == -1001)
                message = @"请求超时";
            else
                message = @"未知错误";
            
            completion(NO,message,nil);
        
    ];
    return task;

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

获取任务数据的上传进度

  • (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];
             else 
                [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification object:downloadTask userInfo:nil];
            

            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
    
    self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite;
    self.downloadProgress.completedUnitCount = totalBytesWritten;



- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
 didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes    
    self.downloadProgress.totalUnitCount = expectedTotalBytes;
    self.downloadProgress.completedUnitCount = fileOffset;

从这些代理方法中可知,设置 AFURLSessionManager 会话实例对象的代码块任务属性,那么这些回调任务对于每一个网络请求任务都是有效的,所以针对于单个特殊的任务回调操作,便不能放在会话管理器的属性中,而是要放在与任务相关联的 AFURLSessionManagerTaskDelegate 代理对象中。
实际使用 AFURLSessionManager 的方法创建网络请求任务时,传递的回调任务,都是在与任务相关联的代理对象的方法中执行的。
以上就是AFURLSessionManager的内容。下面讲解他的子类:AFHTTPSessionManager

(2)AFHTTPSessionManager

发现AFHTTPSessionManager是继承AFURLSessionManager并遵守<NSSecureCoding, NSCopying>
成员属性:
/**
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 * 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 * 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;

方法:

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

请求方式:
GET 请求是向服务端发起请求数据,用来获取或查询资源信息
POST 请求是向服务端发送数据的,用来更新资源信息,它可以改变数据的种类等资源
PUT 请求和POST请求很像,都是发送数据的,但是PUT请求不能改变数据的种类等资源,它只能修改内容
DELETE 请求就是用来删除某个资源的
PATCH 请求和PUT请求一样,也是用来进行数据更新的,它是HTTP verb推荐用于更新的
在实际开发过程中,我们还是使用【GET 和 POST】请求是最多的。

GET请求

- (NSURLSessionDataTask *)GET:(NSString *)URLString
                   parameters:(nullable id)parameters
                      headers:(nullable NSDictionary <NSString *, NSString *> *)headers
                     progress:(nullable void (^)(NSProgress * _Nonnull))downloadProgress
                      success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
                      failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure

    //调用另一个方法构造GET请求
    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                        URLString:URLString
                                                       parameters:parameters
                                                          headers:headers
                                                   uploadProgress:nil
                                                 downloadProgress:downloadProgress
                                                          success:success
                                                          failure:failure];
    

 /*
    启动任务
    使用AFHTTPSessionManager创建的任务默认都帮你启动了,所以不需要手动调用resume方法了。
    上面说的AFURLSessionManager默认没有启动,所以获取任务后要手动启动    */
    [dataTask resume];
    return dataTask;

进一步调用

- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                       URLString:(NSString *)URLString
                                      parameters:(nullable id)parameters
                                         headers:(nullable NSDictionary <NSString *, NSString *> *)headers
                                  uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
                                downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                                         success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                                         failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))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 setValue: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;

同理post请求

- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                             parameters:(nullable id)parameters
                                headers:(nullable NSDictionary <NSString *, NSString *> *)headers
                               progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
                                success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                                failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure

    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters headers:headers uploadProgress:uploadProgress downloadProgress:nil success:success failure:failure];
    
    [dataTask resume];
    return dataTask;

以上是关于源码分析之AFNetworking ①AFURLSessionManager与AFHTTPSessionManager的主要内容,如果未能解决你的问题,请参考以下文章

源码分析之AFNetworking ①AFURLSessionManager与AFHTTPSessionManager

源码分析之AFNetworking④UIkit+AFNetworking

源码分析之AFNetworking④UIkit+AFNetworking

源码分析之AFNetworking④UIkit+AFNetworking

源码分析之AFNetworking④UIkit+AFNetworking

源码分析之AFNetworking ②AFNetworkReachabilityManager