AFNetworking是iOS/OS开发中经常使用的一个第三方网络库,能够说它是目前最流行的网络库,但其代码结构其实并不复杂,也能够说很是简洁优美。在AFNetworking中,大量使用的线程安全的开发技巧,读此源码也是一次很好的多线程学习机会。本篇博客从主要结构和网络请求的主流程进行分享,解析了AFNetworking的设计思路与工做原理,后面还有其中提供的UI扩展包的接口应用总结。javascript
每次读优秀的代码都是一次深入的学习,每一次模仿,都是创造的开始!java
平时咱们在使用AFNetworking框架时,大多只使用其中的请求管理功能。其实,这个有名的框架中还提供了许多其余的工具,除了能够方便的进行网络安全验证,请求数据与回执数据的序列化,网络状态茶台等基础应用外,还提供了UIKit工具包,其中提供有经常使用组件的扩展,图片下载器和缓存器等。json
对于AFNetworking框架的核心,无非AFURLSesstionManager类,这个类是基于系统的NSURLSesstion回话类进行的管理者包装,下图是AF框架一个总体的结构。设计模式
把握这个结构,咱们再来学习AF框架将变得十分容易上手,打开AFURLSesstionManager类,你会发现它有1200多行代码,可是AFURLSesstionManager类真正的实现确实从500多行开始的,以前的代码是内部的代理处理类,就像在MVVM模式中,咱们老是喜欢将控制器的逻辑放入View-Model中同样,AFURLSesstionManager实例也会将通知,回调等操做交给这个代理实例处理。缓存
首先先来看AFURLSesstionManager类的初始化方法:安全
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration { self = [super init]; if (!self) { return nil; } if (!configuration) { //使用默认 configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; } self.sessionConfiguration = configuration; //代理方法执行作在的操做队列 self.operationQueue = [[NSOperationQueue alloc] init]; self.operationQueue.maxConcurrentOperationCount = 1; //建立sesstion会话 self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue]; //默认将返回数据按照json进行解析 self.responseSerializer = [AFJSONResponseSerializer serializer]; //使用默认的安全验证 self.securityPolicy = [AFSecurityPolicy defaultPolicy]; #if !TARGET_OS_WATCH //使用默认的网络状态监测 self.reachabilityManager = [AFNetworkReachabilityManager sharedManager]; #endif //每个请求任务都对应有一个delegate 存在这里面 self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init]; //初始化锁 self.lock = [[NSLock alloc] init]; self.lock.name = AFURLSessionManagerLockName; //下面的作法是为了防止 被修改了sesstion的系统实现有初始任务 [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; }
用AFURLSesstionManager建立请求任务有三类,固然也对应NSURLSesstion中的数据请求任务,上传任务和下载任务。咱们以数据请求任务为例,核心方法以下:服务器
- (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; }
上传任务和下载任务的建立源码和上面大同小异,只是建立出的任务类型不一样,它们都要进行下一步代理的设置,还以数据请求任务的代理设置为例,源码以下:网络
- (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] init]; delegate.manager = self; delegate.completionHandler = completionHandler; dataTask.taskDescription = self.taskDescriptionForSessionTasks; //设置代理 [self setDelegate:delegate forTask:dataTask]; delegate.uploadProgressBlock = uploadProgressBlock; delegate.downloadProgressBlock = downloadProgressBlock; }
须要注意,原生的NSURLSesstionTask的代理其实依然是AFURLSesstionManager类自身,这里Manager做为中介来进行方法的传递(它也会本身处理一些回调,与开发者相关的回调才会转给内部代理处理)。session
上面的流程就是AFURLSesstionManager建立的任务的主流程了,须要注意,它只建立出任务并不会执行,须要开发者手动调用resume才能激活任务。下面的流程就是系统的NSURLSesstionTaskDelagate相关的回调了:多线程
//接收到URL重定向时回调 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler { NSURLRequest *redirectRequest = request; //这里会执行用户自定义的重定向block if (self.taskWillPerformHTTPRedirection) { redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request); } //继续重定向后的请求 if (completionHandler) { completionHandler(redirectRequest); } } //接收到安全认证挑战的回调 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; __block NSURLCredential *credential = nil; //若是开发者有提供block 交开发者处理 不然用默认的安全验证方案处理 if (self.taskDidReceiveAuthenticationChallenge) { disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential); } else { if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { disposition = NSURLSessionAuthChallengeUseCredential; credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; } else { disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; } } else { disposition = NSURLSessionAuthChallengePerformDefaultHandling; } } if (completionHandler) { completionHandler(disposition, credential); } } //须要提供数据流传向服务器时调用 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler { NSInputStream *inputStream = nil; if (self.taskNeedNewBodyStream) { inputStream = self.taskNeedNewBodyStream(session, task); } else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) { inputStream = [task.originalRequest.HTTPBodyStream copy]; } if (completionHandler) { completionHandler(inputStream); } } //已经发送数据后调用 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend { int64_t totalUnitCount = totalBytesExpectedToSend; if(totalUnitCount == NSURLSessionTransferSizeUnknown) { NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"]; if(contentLength) { totalUnitCount = (int64_t) [contentLength longLongValue]; } } if (self.taskDidSendBodyData) { self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount); } } //请求任务完成时回调 - (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 URLSession:session task:task didCompleteWithError:error]; [self removeDelegateForTask:task]; } if (self.taskDidComplete) { self.taskDidComplete(session, task, error); } } //接收到返回数据时回调 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler { NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow; if (self.dataTaskDidReceiveResponse) { disposition = self.dataTaskDidReceiveResponse(session, dataTask, response); } if (completionHandler) { completionHandler(disposition); } } //此请求将要变动成下载任务时回调 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask { //从新设置内部代理 AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask]; if (delegate) { [self removeDelegateForTask:dataTask]; [self setDelegate:delegate forTask:downloadTask]; } if (self.dataTaskDidBecomeDownloadTask) { self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask); } } //开始获取数据时调用 - (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:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler { NSCachedURLResponse *cachedResponse = proposedResponse; if (self.dataTaskWillCacheResponse) { cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse); } if (completionHandler) { completionHandler(cachedResponse); } } //下载任务结束后回调 - (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; //数据位置移动 [[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]; if (error) { [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo]; } return; } } //内部代理进行后续处理 if (delegate) { [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location]; } }
到此,AFURLSesstionManager类的任务基本完成,头文件中的接口更多提供了上述回调的设置还有些通知的发送。下面咱们要来看下内部代理AFURLSesstionManagerTaskDelegate类的做用,须要注意AFURLSesstionManagerTaskDelegate是一个类,并非协议,其除了处理上面介绍的Manager转发过来的回到外,还会进行请求进度的控制。其配置方法和一些监听这里再也不过多介绍,主要来看其对Manager转发过来的回到的处理:
//接收到数据后 将数据进行拼接 - (void)URLSession:(__unused NSURLSession *)session dataTask:(__unused NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { [self.mutableData appendData:data]; } //请求完成后 - (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu" __strong AFURLSessionManager *manager = self.manager; __block id responseObject = nil; //配置一个信息字典 __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer; //拿到请求结果数据 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; 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]; }); }); }); } #pragma clang diagnostic pop } //下载处理 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { NSError *fileManagerError = nil; self.downloadFileURL = nil; if (self.downloadTaskDidFinishDownloading) { self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); if (self.downloadFileURL) { [[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]; if (fileManagerError) { [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo]; } } } }
了解了AFURLSesstionManager,学习AFHTTPSesstionManager将十分轻松,这个类只是前者面向应用的一层封装。咱们能够先从它的接口看起,这也是开发者最熟悉和经常使用的部分。
@interface AFHTTPSessionManager : AFURLSessionManager <NSSecureCoding, NSCopying //基础URL @property (readonly, nonatomic, strong, nullable) NSURL *baseURL; //请求序列化对象 @property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer; //回执序列化对象 @property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer; //建立一个默认的Manager实例 注意不是单例 + (instancetype)manager; //初始化方法 - (instancetype)initWithBaseURL:(nullable NSURL *)url; - (instancetype)initWithBaseURL:(nullable NSURL *)url sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER; //进行get请求 - (nullable NSURLSessionDataTask *)GET:(NSString *)URLString parameters:(nullable id)parameters success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE; - (nullable NSURLSessionDataTask *)GET:(NSString *)URLString parameters:(nullable id)parameters progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; //进行HEAD请求 - (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString parameters:(nullable id)parameters success:(nullable void (^)(NSURLSessionDataTask *task))success failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; //进行POST请求 - (nullable NSURLSessionDataTask *)POST:(NSString *)URLString parameters:(nullable id)parameters success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE; - (nullable NSURLSessionDataTask *)POST:(NSString *)URLString parameters:(nullable id)parameters progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; - (nullable NSURLSessionDataTask *)POST:(NSString *)URLString parameters:(nullable id)parameters constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE; - (nullable NSURLSessionDataTask *)POST:(NSString *)URLString parameters:(nullable id)parameters constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; //进行PUT请求 - (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString parameters:(nullable id)parameters success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; //进行PATCH请求 - (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString parameters:(nullable id)parameters success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; //进行DELETE请求 - (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString parameters:(nullable id)parameters success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
上面全部的请求,一旦建立就会自动resume,开发则不须要额外开启。
AFURLRequestSerialization是一个协议,它的做用其实就是来将请求进行配置,其中只定义了一个接口:
@protocol AFURLRequestSerialization <NSObject, NSSecureCoding, NSCopying> //将请求和参数进行配置后返回 - (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(nullable id)parameters error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW; @end
实现这个接口的类主要是AFHTTPRequestSerizlizaer类,其还有两个子类,分别为AFJSONRequestSerizlizaer和AFPropertyListRequestSerializer类。
在使用AFNetworking进行网络请求时,若是你有过抓包,你必定会发现,在发送的普通HTTP请求的HEAD中默认包含了许多信息,其实这些都是AFHTTPRequestSerizlizaer类作的,他默认会向请求头中添加Accept-Language,User-Agent以及其余可配置的头部信息(缓存,Cookie,超时时间,用户名密码等)。在进行请求参数配置的时候,AFHTTPRequestSerizlizaer会根据请求方法来选择配置到url后面或者添加到请求body中(HEAD,DELETE,GET会追加URL,其余添加body)。AFJSONRequestSerizlizaer的做用与AFHTTPRequestSerizlizaer一致,不一样的是会将请求头中的Content-Type设置为application/json而且将参数格式化成JSON数据放置在请求体中,AFPropertyListRequestSerializer类则是将Content-Type设置为application/x-plist,而且将参数格式化成Plist数据放入请求体。
AFNetworking进行网络请求有一个十分方便的地方在于它能够直接将返回数据进行解析。其中AFHTTPResponseSerializer是最基础的解析类,它只会根据返回头信息来校验返回数据的有效性,整理后直接将原数据返回。AFJSONResponseSerializer类用来解析返回数据为JSON数据的回执,用这个类进行解析时,返回头信息中的MIMEType必须为application/json,text/json或text/javascript。AFXMLParserResponseSerializer类用来解析XML数据,其会返回一个XML解析器,使用它时,返回头信息中的MIMEType必须为application/xml或text/xml。AFXMLDocumentResponseSerializer类将返回数据解析成XML文档。AFPropertyListResponseSerializer用来将返回数据解析成Plist数据。AFImageResponseSerializer类用来将返回数据解析成UIImage图片,其支持的MIMEType类型为image/tiff,image/jpeg,image/gif,image/png,image/ico,image/x-icon,image/bmp,image/x-bmp,image/x-xbitmap,image/x-win-bitmap。
除了上面列出的这些类外,还有一个AFCompoundResponseSerializer类,这个类示例中能够配置多个ResponseSerializer实例,解析的时候会进行遍历尝试找到能够解析的模式,这种模式也叫混合解析模式。
AFAutoPurgingImageCache类是AF框架中提供的图片缓存器,须要注意,它并非一个持久化的缓存工具,只作临时性的缓存。其中封装了自动清缓存和按时间命中的逻辑。
每个AFAutoPurgingImageCache类实例中都有一个缓存池,缓存池有两个临界值,最大容量与指望容量。当实际使用的内存超过最大容量时,缓存池会自动清理到指望容量。在缓存池中,存放的其实是AFCacheImage对象,这个内部类对UIImage进行了包装,以下:
@interface AFCachedImage : NSObject //关联的UIImage @property (nonatomic, strong) UIImage *image; //id @property (nonatomic, strong) NSString *identifier; //图片大小 @property (nonatomic, assign) UInt64 totalBytes; //最后使用时间 @property (nonatomic, strong) NSDate *lastAccessDate; //这个属性没有使用到 @property (nonatomic, assign) UInt64 currentMemoryUsage; @end
清缓存的逻辑十分简单,这里就不作代码解析,流程是每次进行图片缓存时,判断是否超出缓存池最大容量,若是超出,将AFCacheImage对象按照lastAccessDate属性进行排序后进行按顺序删除直到到达指望容量。当收到系统的内存警告时,也会唤起清除内存操做。
AFImageDownloader类专门用来下载图片,其但单独的线程的进行图片的下载处理,而且内置了多任务挂起等待和图片数据缓存的特性。AFImageDownloder类的核心有两个线程:
@property (nonatomic, strong) dispatch_queue_t synchronizationQueue; @property (nonatomic, strong) dispatch_queue_t responseQueue;
其中synchronizationQueue是一个同步线程,用来建立与开始下载任务,也能够理解这个串行线程为这个下载器类的主要代码执行所在的线程,responseQueue是一个并行线程,其用来当请求完成后处理数据。默认状况下,下载器能够同时下载4张图片,若是图片的请求大于4,多出的请求会被暂时挂起,等待其余请求完成在进行激活。其核心流程以下图所示:
如上图所示,AFImageDownloader类中有大量的操做任务池和修改激活任务数的操做,为了保证数据的安全,这也就是为什么AFImageDownloader的主题操做要在其自建的串行线程中执行。核心方法代码解析以下:
- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request withReceiptID:(nonnull NSUUID *)receiptID success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure { __block NSURLSessionDataTask *task = nil; //在串行线程中执行 dispatch_sync(self.synchronizationQueue, ^{ //取当前图片的url做为标识 NSString *URLIdentifier = request.URL.absoluteString; //检查url的可用性 if (URLIdentifier == nil) { if (failure) { NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorBadURL userInfo:nil]; dispatch_async(dispatch_get_main_queue(), ^{ failure(request, nil, error); }); } return; } //检查任务池中是否已经有此任务 AFImageDownloaderMergedTask *existingMergedTask = self.mergedTasks[URLIdentifier]; if (existingMergedTask != nil) { //已经存在此任务 则追加回调 以后返回 这样作的目的是 前后两次对相同图片的请求 能够只进行一次请求,而且执行不一样的两次回调 AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID success:success failure:failure]; [existingMergedTask addResponseHandler:handler]; task = existingMergedTask.task; return; } //进行缓存的检查 switch (request.cachePolicy) { case NSURLRequestUseProtocolCachePolicy: case NSURLRequestReturnCacheDataElseLoad: case NSURLRequestReturnCacheDataDontLoad: { UIImage *cachedImage = [self.imageCache imageforRequest:request withAdditionalIdentifier:nil]; if (cachedImage != nil) { if (success) { dispatch_async(dispatch_get_main_queue(), ^{ success(request, nil, cachedImage); }); } return; } break; } default: break; } //建立数据请求任务 NSUUID *mergedTaskIdentifier = [NSUUID UUID]; NSURLSessionDataTask *createdTask; __weak __typeof__(self) weakSelf = self; createdTask = [self.sessionManager dataTaskWithRequest:request completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) { dispatch_async(self.responseQueue, ^{ __strong __typeof__(weakSelf) strongSelf = weakSelf; AFImageDownloaderMergedTask *mergedTask = self.mergedTasks[URLIdentifier]; if ([mergedTask.identifier isEqual:mergedTaskIdentifier]) { mergedTask = [strongSelf safelyRemoveMergedTaskWithURLIdentifier:URLIdentifier]; if (error) { for (AFImageDownloaderResponseHandler *handler in mergedTask.responseHandlers) { if (handler.failureBlock) { dispatch_async(dispatch_get_main_queue(), ^{ handler.failureBlock(request, (NSHTTPURLResponse*)response, error); }); } } } else { [strongSelf.imageCache addImage:responseObject forRequest:request withAdditionalIdentifier:nil]; for (AFImageDownloaderResponseHandler *handler in mergedTask.responseHandlers) { if (handler.successBlock) { dispatch_async(dispatch_get_main_queue(), ^{ handler.successBlock(request, (NSHTTPURLResponse*)response, responseObject); }); } } } } [strongSelf safelyDecrementActiveTaskCount]; [strongSelf safelyStartNextTaskIfNecessary]; }); }]; //建立处理回调 AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID success:success failure:failure]; //建立图片任务 追加回调 AFImageDownloaderMergedTask *mergedTask = [[AFImageDownloaderMergedTask alloc] initWithURLIdentifier:URLIdentifier identifier:mergedTaskIdentifier task:createdTask]; [mergedTask addResponseHandler:handler]; self.mergedTasks[URLIdentifier] = mergedTask; //进行激活或挂起数据请求任务 if ([self isActiveRequestCountBelowMaximumLimit]) { [self startMergedTask:mergedTask]; } else { [self enqueueMergedTask:mergedTask]; } task = mergedTask.task; }); if (task) { //将回执返回 用来取消任务 return [[AFImageDownloadReceipt alloc] initWithReceiptID:receiptID task:task]; } else { return nil; } }
UIImageView+AFNetworking类别与UIButton+AFNetworking类别是AF中提供了两个加载网络图片的工具类别。这两个类别都为其实例对象关联了一个图片下载器,开发者能够自定义这个下载器也可使用默认提供的,例如:
+ (AFImageDownloader *)sharedImageDownloader { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu" return objc_getAssociatedObject(self, @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance]; #pragma clang diagnostic pop } + (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader { objc_setAssociatedObject(self, @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC); }
虽然头文件中提供了多个设置图片的接口方法,但实际核心方法只有一个,其余方法都是基于这个核心方法时候封装的遍历方法,以UIImageView类别为例,UIButton类别也相似,如图:
进行网络图片设置的过程以下图:
核心方法代码分析:
- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest placeholderImage:(UIImage *)placeholderImage success:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure { //检查请求是否合法 if ([urlRequest URL] == nil) { [self cancelImageDownloadTask]; self.image = placeholderImage; return; } //检查此请求是否正在进行中 if ([self isActiveTaskURLEqualToURLRequest:urlRequest]){ return; } //取消当前UI组件的下载任务 [self cancelImageDownloadTask]; //拿到图片下载器 AFImageDownloader *downloader = [[self class] sharedImageDownloader]; //拿到缓存器 id <AFImageRequestCache> imageCache = downloader.imageCache; //检查缓存图片是否存在 UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil]; if (cachedImage) { if (success) { success(urlRequest, nil, cachedImage); } else { self.image = cachedImage; } [self clearActiveDownloadInformation]; } else { if (placeholderImage) { self.image = placeholderImage; } __weak __typeof(self)weakSelf = self; //取一个随机的UUID做为标志 NSUUID *downloadID = [NSUUID UUID]; //任务控制对象 AFImageDownloadReceipt *receipt; receipt = [downloader downloadImageForURLRequest:urlRequest withReceiptID:downloadID success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) { __strong __typeof(weakSelf)strongSelf = weakSelf; //检查当前的任务id和请求的任务id是否一致 if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) { if (success) { success(request, response, responseObject); } else if(responseObject) { strongSelf.image = responseObject; } //清空任务信息 [strongSelf clearActiveDownloadInformation]; } } failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) { __strong __typeof(weakSelf)strongSelf = weakSelf; if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) { if (failure) { failure(request, response, error); } [strongSelf clearActiveDownloadInformation]; } }]; //存储任务控制对象 self.af_activeImageDownloadReceipt = receipt; } }
AFNetworkActivityIndicatorManager类用来管理系统在状态栏上的指示器显示或隐藏。系统状态栏上的指示器提供的接口很是简单,只有一个:
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
上面方法的参数决定指示器是否显示。AFNetworkActivityIndicatorManager从两个方向来管理这个指示器,能够开发者手动设置,同时他也会对全部AFNetworking发出的请求状态进行监听,来自动适应决定是否显示指示器。
之前我在设计全局Loading时,一般直接为他暴漏显隐两个接口,当处理多个并行请求的时候就很尴尬了,由于你没法保证Loading在最后完成的请求结束后再隐藏。 AFNetworkActivityIndicatorManager采用了触发器的设计模式(其实有些像引用计数),请求来对触发器进行加或减的操做,触发器决定是否触发显示指示器。后面的应用解析中会有具体的接口解释。
在AFNetworking中提供了管理类AFNetworkReachabilityManager类,这个类专门用于网络状态的检测。其代码量不多,接口设计也十分简单。AFNetworkReachabilityManager类解析以下:
//返回默认的单例对象 + (instancetype)sharedManager; //建立一个新的管理对象 + (instancetype)manager; + (instancetype)managerForDomain:(NSString *)domain; + (instancetype)managerForAddress:(const void *)address; - (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability; //获取当前的网络状态 /* typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) { AFNetworkReachabilityStatusUnknown = -1, //未知 AFNetworkReachabilityStatusNotReachable = 0, //未链接 AFNetworkReachabilityStatusReachableViaWWAN = 1, //移动网路 AFNetworkReachabilityStatusReachableViaWiFi = 2, //WIFI }; */ @property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus; //获取当前网络是否链接 @property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable; //获取当前是不是移动网络 @property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN; //获取当前是不是WIFI网络 @property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi; //开启网络状态监听 当网络状态变化时会有回调 - (void)startMonitoring; //中止进行网络状态监听 - (void)stopMonitoring; //设置网络状态发生变化后的回调 - (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block; //格式化的网络状态字符串 - (NSString *)localizedNetworkReachabilityStatusString; //若是开启了网络状态监听 则网络状态发生变化时也会 发这个通知 FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityDidChangeNotification; //通知中userinfo字典中对应网络状态的键名 FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityNotificationStatusItem;
这个类别是AFNetworking中UI部分所提供了一个工具,用来建立远程图片按钮。
//设置共享的下载器对象 用来进行网络图片的下载 + (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader; //获取共享的下载器对象 + (AFImageDownloader *)sharedImageDownloader; //下面这些方法用来设置按钮图片 - (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; //取消按钮图片下载任务 - (void)cancelImageDownloadTaskForState:(UIControlState)state; //取消按钮背景图片下载任务 - (void)cancelBackgroundImageDownloadTaskForState:(UIControlState)state;
这个类别是AFNetworking框架提供了UIImageView加载异步图片的方案(更多时候可能会用SDWebImage)。
//设置共享的图片下载器 + (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader; //获取共享的图片下载器 + (AFImageDownloader *)sharedImageDownloader; //设置图片 - (void)setImageWithURL:(NSURL *)url; - (void)setImageWithURL:(NSURL *)url placeholderImage:(nullable UIImage *)placeholderImage; - (void)setImageWithURLRequest:(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)cancelImageDownloadTask;
AFNetworking的UI工具包中提供了AFNetworkActivityIndicatorManager类,这个管理类用来对iOS设备状态栏上活动指示器的显示隐藏进行管理。其提供的接口十分简单,解析以下:
//设置是否有效 /* 若是设置为YES,则能够手动进行控制器的控制 若是设置为NO,则控制器只会根据网络通知来绝对是否显示 */ @property (nonatomic, assign, getter = isEnabled) BOOL enabled; //指示器当前是否显示 @property (readonly, nonatomic, assign, getter=isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible; //指示器的延时显示时间 当多久后若是网络请求尚未完成 再进行显示 @property (nonatomic, assign) NSTimeInterval activationDelay; //设置当请求结束多久后 活动指示器隐藏 默认为0.17s @property (nonatomic, assign) NSTimeInterval completionDelay; //获取单例对象 + (instancetype)sharedManager; //手动对触发器加1 //当计数大于0 则显示指示器 - (void)incrementActivityCount; //手动对触发器减1 //当计数不大于0 则隐藏指示器 - (void)decrementActivityCount; //设置指示器状态改变的回调 - (void)setNetworkingActivityActionWithBlock:(nullable void (^)(BOOL networkActivityIndicatorVisible))block;
这两个类别能够将一个请求任务绑定到活动指示器或原生刷新组件上,任务的完成与否来决定控件是否展现动画。
//UIActivityIndicatorView+AFNetworking //将任务绑定到活动指示器上 - (void)setAnimatingWithStateOfTask:(nullable NSURLSessionTask *)task; //UIRefresh+AFNetworing //将任务绑定到刷新组件上 - (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task;
这个类别能够将上传和下载任务绑定到进度条组件上。
//将上传任务绑定 - (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task animated:(BOOL)animated; //将下载任务绑定 - (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task animated:(BOOL)animated;
这个类别是对UIWebView的一种扩展(因为WebKit,这个类别不多会用到了),其主要做用是将WebView的直接加载改成先下载本地数据,而后进行本地数据的加载,并能够提供一个进度。
//这个方法是下面方法的封装 - (void)loadRequest:(NSURLRequest *)request progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress success:(nullable NSString * (^)(NSHTTPURLResponse *response, NSString *HTML))success failure:(nullable void (^)(NSError *error))failure; //进行网页数据请求,完成后会将网页数据返回 而且自动的进行加载 - (void)loadRequest:(NSURLRequest *)request MIMEType:(nullable NSString *)MIMEType textEncodingName:(nullable NSString *)textEncodingName progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress success:(nullable NSData * (^)(NSHTTPURLResponse *response, NSData *data))success failure:(nullable void (^)(NSError *error))failure;
AFImageDownloader是AF框架中提供的图片下载管理类。其内部封装了一个AFImageDownloadReceipt回执类,这个类实例用来进行正在下载图片任务的取消。AFImageDownloader解析以下:
//图片缓存器 @property (nonatomic, strong, nullable) id <AFImageRequestCache> imageCache; //请求会话管理类 @property (nonatomic, strong) AFHTTPSessionManager *sessionManager; //设置下载器属性 /* 下载器能够设置同时下载图片的最大数量 若是超出数量的请求会被暂时挂起 typedef NS_ENUM(NSInteger, AFImageDownloadPrioritization) { AFImageDownloadPrioritizationFIFO,//被挂起的请求遵照先挂起的先开始 AFImageDownloadPrioritizationLIFO//被挂起的请求遵照后挂起的先开始 }; */ @property (nonatomic, assign) AFImageDownloadPrioritization downloadPrioritizaton; //获取单例对象 + (instancetype)defaultInstance; //获取默认的url缓存器 + (NSURLCache *)defaultURLCache; //使用初始化方法新建对象 - (instancetype)initWithSessionManager:(AFHTTPSessionManager *)sessionManager downloadPrioritization:(AFImageDownloadPrioritization)downloadPrioritization maximumActiveDownloads:(NSInteger)maximumActiveDownloads imageCache:(nullable id <AFImageRequestCache>)imageCache; //进行图片请求 随机生成uuid - (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure; //和上面方法同样,使用自定义的uuid - (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request withReceiptID:(NSUUID *)receiptID success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure;
AF框架中UIKit工具包里的ADAutoPurgingImageCache类用来进行图片缓存,其还封装了自动清缓存的逻辑,缓存的命中则是彻底按照使用时间前后为标准。类接口解析以下:
//图片缓存协议 @protocol AFImageCache <NSObject> //根据id来缓存图片 - (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier; //根据id来删除图片 - (BOOL)removeImageWithIdentifier:(NSString *)identifier; //删除全部图片缓存 - (BOOL)removeAllImages; //根据id来取缓存 - (nullable UIImage *)imageWithIdentifier:(NSString *)identifier; @end //网络图片缓存协议 @protocol AFImageRequestCache <AFImageCache> //缓存某个url的图片 用url+id的方式 - (void)addImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier; //移除某个缓存图片 - (BOOL)removeImageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier; //获取缓存 - (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier; @end //缓存器实例类 @interface AFAutoPurgingImageCache : NSObject <AFImageRequestCache> //缓存空间大小 默认为100M @property (nonatomic, assign) UInt64 memoryCapacity; //当缓存超过最大限制时进行清缓存 清缓存后的缓存底线大小设置 默认60M @property (nonatomic, assign) UInt64 preferredMemoryUsageAfterPurge; //已用空间大小 @property (nonatomic, assign, readonly) UInt64 memoryUsage; //初始化方法 - (instancetype)init; - (instancetype)initWithMemoryCapacity:(UInt64)memoryCapacity preferredMemoryCapacity:(UInt64)preferredMemoryCapacity; @end
每次读优秀的代码都是一次深入的学习,每一次模仿,都是创造的开始!
——QQ 316045346 欢迎交流