###AFNetWorking ****下载图片过程
[self.imageView setImageWithURL:[NSURL URLWithString:item.picture] placeholderImage:[UIImage imageNamed:@"PTV_Normal_Default_Icon"]];
比如上面的执行过程:
会调用如下:
- (void)setImageWithURL:(NSURL *)url
placeholderImage:(UIImage *)placeholderImage
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request addValue:@"image/*" forHTTPHeaderField:@"Accept"];
[self setImageWithURLRequest:request placeholderImage:placeholderImage success:nil failure:nil];
}
生成 request,并设置http 请求头 accept 字段值为 image/*
我们都知道,http header 消息通常被分为4个部分:general header, request header, response header, entity header。但是这种分法就理解而言,感觉界限不太明确。根据维基百科对http header内容的组织形式,大体分为Request和Response两部分。
<strong>Accept 字段 表示:指定客户端能够接收的内容类型 </strong>
http 头中还有一个比较常用的字段 Cache-Control,用来指定缓存策略。
关于 http 头的,详见 httpHead详解 w3c官网Header Field Definitions
扯远了,不过看一遍还是有帮助的。
下载过程如下:
- (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;
}
[self cancelImageDownloadTask];
AFImageDownloader *downloader = [[self class] sharedImageDownloader];
id <AFImageRequestCache> imageCache = downloader.imageCache;
//Use the image from the image cache if it exists
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;
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;
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;
}
}
###****接下来分析一下上面那个长函数
AFNetworking 的所有图片下载都是通过同一个downloader 实例完成的,这里用的是默认的。
AFImageDownloader *downloader = [[self class] sharedImageDownloader];
+ (AFImageDownloader *)sharedImageDownloader {
return objc_getAssociatedObject(self, @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance];
}
+ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader {
objc_setAssociatedObject(self, @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
这里通过关联和 class 绑定的。这里的 [AFImageDownloader defaultInstance] 就有一些默认的配置。比如 defaultURLCache等等。
但是下载过程还是通过AF的组件 AFHTTPSessionManager 来完成的。
- (instancetype)init {
NSURLSessionConfiguration *defaultConfiguration = [self.class defaultURLSessionConfiguration];
AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:defaultConfiguration];
sessionManager.responseSerializer = [AFImageResponseSerializer serializer];
return [self initWithSessionManager:sessionManager
downloadPrioritization:AFImageDownloadPrioritizationFIFO
maximumActiveDownloads:4
imageCache:[[AFAutoPurgingImageCache alloc] init]];
}
最重要的,这里解析数据的指定成了图片解析。
sessionManager.responseSerializer = [AFImageResponseSerializer serializer];
AFImageResponseSerializer 提供了图片解码的过程。
AFHTTPSessionManager 优秀的设计方案
@property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;
@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;
这里请求和响应的property 都是遵守某个协议的,方便对不同类型的请求做不同的处理,比如:xml json, image. <strong>这在我们平常的类中可以参考</strong>
获取到了下载实例,接下来尝试从缓存中获取图片
UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil];
if (cachedImage) {
if (success) {
success(urlRequest, nil, cachedImage);
} else {
self.image = cachedImage;
}
//代表下载完成。
[self clearActiveDownloadInformation];
如果没有缓存,就通过AFImageDownloader 下载数据,因为解析出来的数据已经处理过了,所以直接赋值image 就可以了!
###****缓存篇
这里缓存默认使用的是 NSCache..
+ (NSURLCache *)defaultURLCache {
// It's been discovered that a crash will occur on certain versions
// of iOS if you customize the cache.
//
// More info can be found here: https://devforums.apple.com/message/1102182#1102182
//
// When iOS 7 support is dropped, this should be modified to use
// NSProcessInfo methods instead.
if ([[[UIDevice currentDevice] systemVersion] compare:@"8.2" options:NSNumericSearch] == NSOrderedAscending) {
return [NSURLCache sharedURLCache];
}
return [[NSURLCache alloc] initWithMemoryCapacity:20 * 1024 * 1024
diskCapacity:150 * 1024 * 1024
diskPath:@"com.alamofire.imagedownloader"];
}
我在本地项目试了一下如下测试代码:
NSLog(@"🐒🐒🐒🐒 %lu %lu",(unsigned long)[NSURLCache sharedURLCache].memoryCapacity, (unsigned long)[NSURLCache sharedURLCache].diskCapacity);
输出如下:
2017-09-06 17:30:32.928311+0800 PandaTV-HD[1173:464474] 🐒🐒🐒🐒 512000 10000000
也就是说,其实默认就已经设置好了512kb的内存缓存空间,以及10MB的磁盘缓存空间。可能你的代码中并没有写任何与NSURLCache有关的东西,但其实它已经默默的开始帮你进行缓存了。
关于 NSCache 详见两篇比较好的博客 DIY图片缓存库 NSUrlCache详解
###****下载队列篇
下载队列创建如下:
self.synchronizationQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_SERIAL);
创建的是一个串行队列。
配合
typedef NS_ENUM(NSInteger, AFImageDownloadPrioritization) {
AFImageDownloadPrioritizationFIFO,
AFImageDownloadPrioritizationLIFO
};
可以实现,下载队列的顺序,比如:
- (void)enqueueMergedTask:(AFImageDownloaderMergedTask *)mergedTask {
switch (self.downloadPrioritizaton) {
case AFImageDownloadPrioritizationFIFO:
[self.queuedMergedTasks addObject:mergedTask];
break;
case AFImageDownloadPrioritizationLIFO:
[self.queuedMergedTasks insertObject:mergedTask atIndex:0];
break;
}
}
self.queuedMergedTasks 是一个数组。
在上个一请求完成,会尝试开启下一个请求。
- (void)safelyStartNextTaskIfNecessary {
dispatch_sync(self.synchronizationQueue, ^{
if ([self isActiveRequestCountBelowMaximumLimit]) {
while (self.queuedMergedTasks.count > 0) {
AFImageDownloaderMergedTask *mergedTask = [self dequeueMergedTask];
if (mergedTask.task.state == NSURLSessionTaskStateSuspended) {
[self startMergedTask:mergedTask];
break;
}
}
}
});
}
请求都是在同一个线程里 同步进行的,所以不用使用NSLock 这样子的东东。