代码:https://github.com/xufeng79x/SDWebImage
1.简介
SDWebImage是一个第三方框架,它能够帮助我们有效管理应用图片下载,沙盒保存和内存保存的任务。通过这篇文章将了解到它的原理和一般使用方式。
2.图片获取
记得在[New learn] 设计模式文章中我们讨论了通过LibraryAPI类将HTTPClient和PersistencyManager包装起来进行图片的下载,基本原理为:
当本地沙盒中存在对应图片文件时候load本地文件否则回去网上下载并保存在本地以备后续使用。这里我们在增加一个内存缓存,将图片缓存在内存中:
增加图片缓存:
@interface PersistencyManager () { // an array of all albums NSMutableArray *albums; // 图片缓存 NSMutableDictionary *imageCache; } @end
随后在图片保存以及在图片从沙盒读取的时候讲image信息放入其中,等待下次再去读取相同图片的时候就直接会去缓存中读取。
- (void)saveImage:(UIImage*)image filename:(NSString*)filename { // 进到这里可以认为图片刚从往下下载了 NSLog(@"Get the image:%@ from internet!", filename); // 将图片放到缓存中 [imageCache setObject:image forKey:filename]; 。。。。。。 } - (UIImage*)getImage:(NSString*)filename { NSString *fileName = filename; // 先从缓存中获取数据,如果有就直接返回 UIImage *image = [imageCache objectForKey:filename]; if (image) { NSLog(@"Get the image:%@ from cache!", filename); return image; } 。。。。。 // 当图片在沙盒中,但是没有在缓存中,将图片放到缓存 if (image) { NSLog(@"Get the image:%@ from sandbox!", fileName); // 将图片放到缓存中 [imageCache setObject:image forKey:fileName]; } return image; }
测试
1.获取沙盒路径:
(lldb) po NSHomeDirectory() /Users/apple/Library/Developer/CoreSimulator/Devices/8E2826AC-C429-4A39-B0EE-C261E155E191/data/Containers/Data/Application/0251E391-F9DD-4BA3-AA81-74D35F9613B5 (lldb)
2.前往沙盒,删除沙盒中已经存在的图片:
3.运行程序,此时由于缓存和沙盒中并没有图片信息,他将从网络上下载图片。
BlueLibrary[:] Get the image:d000baa1cd11728b0d5fc2bec9fcc3cec3fd2c3f.jpg from internet! -- :::] Get the image:.jpg from internet! -- :::] Get the image:.jpg from internet! -- :::] Get the image:.jpg from internet! -- :::] Get the image:.jpg from internet!
沙盒文件夹中会生成图:
4.当点击删除按钮的时候,程序会重新load 封面图片,这个时候回从哪里读取图片呢?
-- :::] Get the image:.jpg from cache! -- :::] Get the image:d000baa1cd11728b0d5fc2bec9fcc3cec3fd2c3f.jpg from cache! -- :::] Get the image:.jpg from cache! -- :::] Get the image:.jpg from cache!
可以看到都是从缓存中将图片读取出来,这是由于网络下载图片后会在沙盒生成文件并将在缓存中保存这些图片。
5.当关闭程序,在运行应用的时候,应该会从沙盒中读取,因为缓存中由于重启的原因不会有数据。
-- :::] Get the image:.jpg from sandbox! -- :::] Get the image:d000baa1cd11728b0d5fc2bec9fcc3cec3fd2c3f.jpg from sandbox! -- :::] Get the image:.jpg from sandbox! -- :::] Get the image:.jpg from sandbox! -- :::] Get the image:.jpg from sandbox!
上述流程总结:
在这节中我们自己实现了图片的下载,保存和缓存。大致的实现原理如下:
其中在我们的应用中还不会出现多线程操作的重复问题,如果有这个场景,那么我们就需要在新建一个线程缓存区判断下载指定图片的线程是否存在,如果存在就不在进行重复操作。
3.使用框架SDWebImage
上述的过程复杂繁琐,在iOS开发世界中,我们同样可以使用很多第三方框架,SDWebImage框架就是解决我们上述场景的解决方案。
3.1 什么是SDWebImage
官网:https://github.com/rs/SDWebImage
3.2 如何使用
下载框架:进入官网,点击Download ZIP
导入框架:将框架文件夹拷贝到工程中
使用框架:
框架为我们提供丰富的API和属性参数。
直接使用框架方法来代替现有代码:
- (void)downloadImage:(NSNotification*)notification { UIImageView *imageView = notification.userInfo[@"imageView"]; NSString *coverUrl = notification.userInfo[@"coverUrl"];
// 框架的词方法的功能已经可以替代注释掉的代码的功能
[imageView sd_setImageWithURL:[NSURL URLWithString:coverUrl] placeholderImage:nil];
// // 2 从缓存或者沙盒中读取图片 // imageView.image = [persistencyManager getImage:[coverUrl lastPathComponent]]; // // if (imageView.image == nil) // { // // 3 当缓存或者沙盒中图片不存在的时候,远程多线程下载图片。 // dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // UIImage *image = [httpClient downloadImage:coverUrl]; // // // 4 图片下载完毕后需要在主线程中更行UI,并保存图片到沙盒和刷新缓存 // dispatch_sync(dispatch_get_main_queue(), ^{ // imageView.image = image; // [persistencyManager saveImage:image filename:[coverUrl lastPathComponent]]; // }); // }); // } }
将沙盒中的图片文件删除,重启应用。
非常不幸,和我一样遇到了如下的问题吗?
Undefined symbols for architecture x86_64: "_CGImageSourceCopyPropertiesAtIndex", referenced from: +[UIImage(MultiFormat) sd_imageOrientationFromImageData:] in UIImage+MultiFormat.o -[SDWebImageDownloaderOperation connection:didReceiveData:] in SDWebImageDownloaderOperation.o +[UIImage(GIF) sd_frameDurationAtIndex:source:] in UIImage+GIF.o "_CGImageSourceCreateImageAtIndex", referenced from: -[SDWebImageDownloaderOperation connection:didReceiveData:] in SDWebImageDownloaderOperation.o +[UIImage(GIF) sd_animatedGIFWithData:] in UIImage+GIF.o "_CGImageSourceCreateWithData", referenced from: +[UIImage(MultiFormat) sd_imageOrientationFromImageData:] in UIImage+MultiFormat.o -[SDWebImageDownloaderOperation connection:didReceiveData:] in SDWebImageDownloaderOperation.o +[UIImage(GIF) sd_animatedGIFWithData:] in UIImage+GIF.o "_CGImageSourceGetCount", referenced from: +[UIImage(GIF) sd_animatedGIFWithData:] in UIImage+GIF.o "_OBJC_CLASS_$_MKAnnotationView", referenced from: l_OBJC_$_CATEGORY_MKAnnotationView_$_WebCache in MKAnnotationView+WebCache.o l_OBJC_$_CATEGORY_MKAnnotationView_$_WebCacheDeprecated in MKAnnotationView+WebCache.o "_kCGImagePropertyGIFDelayTime", referenced from: +[UIImage(GIF) sd_frameDurationAtIndex:source:] in UIImage+GIF.o "_kCGImagePropertyGIFDictionary", referenced from: +[UIImage(GIF) sd_frameDurationAtIndex:source:] in UIImage+GIF.o "_kCGImagePropertyGIFUnclampedDelayTime", referenced from: +[UIImage(GIF) sd_frameDurationAtIndex:source:] in UIImage+GIF.o "_kCGImagePropertyOrientation", referenced from: +[UIImage(MultiFormat) sd_imageOrientationFromImageData:] in UIImage+MultiFormat.o -[SDWebImageDownloaderOperation connection:didReceiveData:] in SDWebImageDownloaderOperation.o "_kCGImagePropertyPixelHeight", referenced from: -[SDWebImageDownloaderOperation connection:didReceiveData:] in SDWebImageDownloaderOperation.o "_kCGImagePropertyPixelWidth", referenced from: -[SDWebImageDownloaderOperation connection:didReceiveData:] in SDWebImageDownloaderOperation.o ld: symbol(s) not found for architecture x86_64
解决方法:发生此问题的原因为框架还依赖于其他框架http://*.com/questions/12306161/build-fail-when-using-sdwebimage
验证:
同时在沙盒中发现框架已经将图片存入了特定的文件夹中:
4. SDWebImage再研究:
在测试中我们使用到了一个API方法,通过URL来获取图片的方法内部其实都调用了以下实现方法
/** * Set the imageView `image` with an `url`, placeholder and custom options. * * The download is asynchronous and cached. * * @param url The url for the image. * @param placeholder The image to be set initially, until the image request finishes. * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. * @param progressBlock A block called while image is downloading * @param completedBlock A block called when operation has been completed. This block has no return value * and takes the requested UIImage as first parameter. In case of error the image parameter * is nil and the second parameter may contain an NSError. The third parameter is a Boolean * indicating if the image was retrieved from the local cache or from the network. * The fourth parameter is the original image url. */ - (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock;
它能够制定图片占位符号,操作选项(是否缓存图片等),下载中处理和下载后处理块。
除此之外,对于gif图片来说,和调用普通图片并没有任何差别。
原理:
其实此框架的原理和我们前面自己实现的原理是一个道理,它也有缓存,沙盒存储和网络下载三部分。我们可以通过SDImageCache.m定义的如下方法可见一斑:
#if TARGET_OS_IPHONE // Subscribe to app events [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(clearMemory) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cleanDisk) name:UIApplicationWillTerminateNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backgroundCleanDisk) name:UIApplicationDidEnterBackgroundNotification object:nil]; #endif
详见:https://github.com/rs/SDWebImage
以上。