IOS网络图片缓存详解
来源:互联网 发布:区域电网排放因子数据 编辑:程序博客网 时间:2024/05/21 18:47
在开发移动应用的时候比如Android,IOS,因为手机流量、网速、内存等这些因素,当我们的移动应用是针对互联网,并要频繁访问网络的话,对网络优化这块就显得尤为重要了。
比如某个应用要经常显示网络图片,就不能每次显示图片都去网络上下载,那太耗费时间也太耗费流量,这时就要对网络图片进行缓存了,以下是我对IOS网络图片缓存的一些见解,有不足之处,欢迎大家指出来,一起探讨。
处理网络图片缓存步骤:
1、根据图片URL查找内存是否有这张图片,有则返回图片,没有则进入第二步
2、查找物理存储是否有这张图片,有则返回图片,没有则进入第三步
3、从网络上下载该图片,下载完后保存到内存和物理存储上,并返回该图片
注:因为URL包含特殊字符和长度不确定,要对URL进行MD5处理或其他处理
下面是针对以上步骤的代码讲解:
1、内存缓存图片处理
使用NSMutableDictionary存储图片UIImage,数组的Key为该图片的URL地址
//缓存图片到内存上[memCache setObject:image forKey:key];
2、物理缓存图片处理把图片保持到物理存储设备上,则直接使用NSFileManager,把URL作为文件名保存
3、网络图片下载处理
图片使用异步下载,下载完后把图片保持到NSMutableDictionary和物理存储上
以下是摘自SDWebImageleik网络图片缓存处理的一个类,有详细注释
.h文件
@interface SDImageCache : NSObject{ NSMutableDictionary *memCache;//内存缓存图片引用 NSString *diskCachePath;//物理缓存路径 NSOperationQueue *cacheInQueue, *cacheOutQueue;}+ (SDImageCache *)sharedImageCache;//保存图片- (void)storeImage:(UIImage *)image forKey:(NSString *)key;//保存图片,并选择是否保存到物理存储上- (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk;//保存图片,可以选择把NSData数据保存到物理存储上- (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)key toDisk:(BOOL)toDisk;//通过key返回UIImage- (UIImage *)imageFromKey:(NSString *)key;//如果获取内存图片失败,是否可以在物理存储上查找- (UIImage *)imageFromKey:(NSString *)key fromDisk:(BOOL)fromDisk;- (void)queryDiskCacheForKey:(NSString *)key delegate:(id <SDImageCacheDelegate>)delegate userInfo:(NSDictionary *)info;//清除key索引的图片- (void)removeImageForKey:(NSString *)key;//清除内存图片- (void)clearMemory;//清除物理缓存- (void)clearDisk;//清除过期物理缓存- (void)cleanDisk;@end
.m文件
@implementation SDImageCache#pragma mark NSObject- (id)init{ if ((self = [super init])) { // Init the memory cache memCache = [[NSMutableDictionary alloc] init]; // Init the disk cache NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); diskCachePath = [[[paths objectAtIndex:0] stringByAppendingPathComponent:@"ImageCache"] retain]; if (![[NSFileManager defaultManager] fileExistsAtPath:diskCachePath]) { [[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL]; } // Init the operation queue cacheInQueue = [[NSOperationQueue alloc] init]; cacheInQueue.maxConcurrentOperationCount = 1; cacheOutQueue = [[NSOperationQueue alloc] init]; cacheOutQueue.maxConcurrentOperationCount = 1; #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]; #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_0 UIDevice *device = [UIDevice currentDevice]; if ([device respondsToSelector:@selector(isMultitaskingSupported)] && device.multitaskingSupported) { // When in background, clean memory in order to have less chance to be killed [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(clearMemory) name:UIApplicationDidEnterBackgroundNotification object:nil]; }#endif#endif } return self;}- (void)dealloc{ [memCache release], memCache = nil; [diskCachePath release], diskCachePath = nil; [cacheInQueue release], cacheInQueue = nil; [[NSNotificationCenter defaultCenter] removeObserver:self]; [super dealloc];}#pragma mark SDImageCache (class methods)+ (SDImageCache *)sharedImageCache{ if (instance == nil) { instance = [[SDImageCache alloc] init]; } return instance;}#pragma mark SDImageCache (private)/* *创建指定图片key的路径 */- (NSString *)cachePathForKey:(NSString *)key{ const char *str = [key UTF8String]; unsigned char r[CC_MD5_DIGEST_LENGTH]; CC_MD5(str, (CC_LONG)strlen(str), r); NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15]]; return [diskCachePath stringByAppendingPathComponent:filename];}/* *保存key和Data到物理存储 *keyAndData[0] ->key *keyAndData[1] ->Data */- (void)storeKeyWithDataToDisk:(NSArray *)keyAndData{ // Can't use defaultManager another thread NSFileManager *fileManager = [[NSFileManager alloc] init]; NSString *key = [keyAndData objectAtIndex:0]; NSData *data = [keyAndData count] > 1 ? [keyAndData objectAtIndex:1] : nil; //如果有数据,则保存到物理存储上 if (data) { [fileManager createFileAtPath:[self cachePathForKey:key] contents:data attributes:nil]; } else { //如果没有data,则把UIImage转换为JPEG,并保存到物理存储上 // If no data representation given, convert the UIImage in JPEG and store it // This trick is more CPU/memory intensive and doesn't preserve alpha channel UIImage *image = [[self imageFromKey:key fromDisk:YES] retain]; // be thread safe with no lock if (image) {#if TARGET_OS_IPHONE [fileManager createFileAtPath:[self cachePathForKey:key] contents:UIImageJPEGRepresentation(image, (CGFloat)1.0) attributes:nil];#else NSArray* representations = [image representations]; NSData* jpegData = [NSBitmapImageRep representationOfImageRepsInArray: representations usingType: NSJPEGFileType properties:nil]; [fileManager createFileAtPath:[self cachePathForKey:key] contents:jpegData attributes:nil];#endif [image release]; } } [fileManager release];}/* *查找图片委托 */- (void)notifyDelegate:(NSDictionary *)arguments{ NSString *key = [arguments objectForKey:@"key"]; id <SDImageCacheDelegate> delegate = [arguments objectForKey:@"delegate"]; NSDictionary *info = [arguments objectForKey:@"userInfo"]; UIImage *image = [arguments objectForKey:@"image"]; if (image) { [memCache setObject:image forKey:key]; if ([delegate respondsToSelector:@selector(imageCache:didFindImage:forKey:userInfo:)]) { [delegate imageCache:self didFindImage:image forKey:key userInfo:info]; } } else { if ([delegate respondsToSelector:@selector(imageCache:didNotFindImageForKey:userInfo:)]) { [delegate imageCache:self didNotFindImageForKey:key userInfo:info]; } }}/* *查找物理缓存上的图片 */- (void)queryDiskCacheOperation:(NSDictionary *)arguments{ NSString *key = [arguments objectForKey:@"key"]; NSMutableDictionary *mutableArguments = [[arguments mutableCopy] autorelease]; UIImage *image = [[[UIImage alloc] initWithContentsOfFile:[self cachePathForKey:key]] autorelease]; if (image) {#ifdef ENABLE_SDWEBIMAGE_DECODER UIImage *decodedImage = [UIImage decodedImageWithImage:image]; if (decodedImage) { image = decodedImage; }#endif [mutableArguments setObject:image forKey:@"image"]; } [self performSelectorOnMainThread:@selector(notifyDelegate:) withObject:mutableArguments waitUntilDone:NO];}#pragma mark ImageCache/* *缓存图片 * **/- (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)key toDisk:(BOOL)toDisk{ if (!image || !key) { return; } //缓存图片到内存上 [memCache setObject:image forKey:key]; //如果需要缓存到物理存储上,并data不为空,则把data缓存到物理存储上 if (toDisk) { if (!data) return; NSArray *keyWithData; if (data) { keyWithData = [NSArray arrayWithObjects:key, data, nil]; } else { keyWithData = [NSArray arrayWithObjects:key, nil]; } //后台线程缓存图片到物理存储上 [cacheInQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(storeKeyWithDataToDisk:) object:keyWithData] autorelease]]; }}/* *保存图片到内存上,不保存到物理存储上 */- (void)storeImage:(UIImage *)image forKey:(NSString *)key{ [self storeImage:image imageData:nil forKey:key toDisk:YES];}/* *保存图片到内存上,不保存到物理存储上 */- (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk{ [self storeImage:image imageData:nil forKey:key toDisk:toDisk];}/* *通过key返回指定图片 */- (UIImage *)imageFromKey:(NSString *)key{ return [self imageFromKey:key fromDisk:YES];}/* *返回一张图像 *key:图像的key *fromDisk:如果内存中没有图片,是否在物理存储上查找 *return 返回查找到的图片,如果没有则返回nil */- (UIImage *)imageFromKey:(NSString *)key fromDisk:(BOOL)fromDisk{ if (key == nil) { return nil; } UIImage *image = [memCache objectForKey:key]; if (!image && fromDisk) //如果内存没有图片,并且可以在物理存储上查找,则返回物理存储上的图片 { image = [[[UIImage alloc] initWithContentsOfFile:[self cachePathForKey:key]] autorelease]; if (image) { [memCache setObject:image forKey:key]; } } return image;}- (void)queryDiskCacheForKey:(NSString *)key delegate:(id <SDImageCacheDelegate>)delegate userInfo:(NSDictionary *)info{ if (!delegate) { return; } if (!key) { if ([delegate respondsToSelector:@selector(imageCache:didNotFindImageForKey:userInfo:)]) { [delegate imageCache:self didNotFindImageForKey:key userInfo:info]; } return; } // First check the in-memory cache... UIImage *image = [memCache objectForKey:key]; if (image) { // ...notify delegate immediately, no need to go async if ([delegate respondsToSelector:@selector(imageCache:didFindImage:forKey:userInfo:)]) { [delegate imageCache:self didFindImage:image forKey:key userInfo:info]; } return; } NSMutableDictionary *arguments = [NSMutableDictionary dictionaryWithCapacity:3]; [arguments setObject:key forKey:@"key"]; [arguments setObject:delegate forKey:@"delegate"]; if (info) { [arguments setObject:info forKey:@"userInfo"]; } [cacheOutQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(queryDiskCacheOperation:) object:arguments] autorelease]];}/* *从内存和物理存储上移除指定图片 */- (void)removeImageForKey:(NSString *)key{ if (key == nil) { return; } [memCache removeObjectForKey:key]; [[NSFileManager defaultManager] removeItemAtPath:[self cachePathForKey:key] error:nil];}/* *清除内存缓存区的图片 */- (void)clearMemory{ [cacheInQueue cancelAllOperations]; // won't be able to complete [memCache removeAllObjects];}/* *清除物理存储上的图片 */- (void)clearDisk{ [cacheInQueue cancelAllOperations]; [[NSFileManager defaultManager] removeItemAtPath:diskCachePath error:nil]; [[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];}/* *清除过期缓存的图片 */- (void)cleanDisk{ NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-cacheMaxCacheAge]; NSDirectoryEnumerator *fileEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:diskCachePath]; for (NSString *fileName in fileEnumerator) { NSString *filePath = [diskCachePath stringByAppendingPathComponent:fileName]; NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil]; if ([[[attrs fileModificationDate] laterDate:expirationDate] isEqualToDate:expirationDate]) { [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil]; } }}@end
在网络图片缓存的处理上,有个比好的第三方类库“SDWebImage”,点击下载SDWebImage。
原文地址:http://blog.csdn.net/kqjob
- IOS网络图片缓存详解
- IOS网络图片缓存详解
- IOS网络图片缓存详解
- IOS网络图片缓存详解
- IOS网络图片缓存详解
- IOS网络图片缓存详解
- iOS网络图片缓存详解
- IOS网络图片缓存详解
- IOS网络图片缓存详解
- IOS网络图片缓存详解
- IOS网络图片缓存详解
- IOS网络图片缓存详解
- IOS网络图片缓存详解
- IOS网络图片缓存详解
- iOS 图片缓存 详解
- 【iOS】网络加载图片缓存与SDWebImage
- 【iOS】网络加载图片缓存与SDWebImage
- 【iOS】网络加载图片缓存与SDWebImage
- Linux shell的标准输入、输出和错误
- vi ---> emacs
- loadrunner--loadrunner自动关联
- 纪念这一天 2013年8月19号
- 常用字符串处理函数
- IOS网络图片缓存详解
- Vi编辑器的复制操作
- Sql Server - How to write a Stored procedure in Sql server
- 详细分析Linux df命令的使用方法
- V4L2驱动程序架构
- java父类构造方法的调用
- Codeforces Round #196 contest/338 div1
- Overview of SQL Server Stored Procedure
- poj1003 Hangover 解题报告