您的位置:首页 > 理论基础 > 计算机网络

IOS网络图片缓存详解

2015-12-25 20:32 549 查看
在开发移动应用的时候比如Android,IOS,因为手机流量、网速、内存等这些因素,当我们的移动应用是针对互联网,并要频繁访问网络的话,对网络优化这块就显得尤为重要了。

比如某个应用要经常显示网络图片,就不能每次显示图片都去网络上下载,那太耗费时间也太耗费流量,这时就要对网络图片进行缓存了,以下是我对IOS网络图片缓存的一些见解,有不足之处,欢迎大家指出来,一起探讨。

处理网络图片缓存步骤:

1、根据图片URL查找内存是否有这张图片,有则返回图片,没有则进入第二步

2、查找物理存储是否有这张图片,有则返回图片,没有则进入第三步

3、从网络上下载该图片,下载完后保存到内存和物理存储上,并返回该图片

注:因为URL包含特殊字符和长度不确定,要对URL进行MD5处理或其他处理

下面是针对以上步骤的代码讲解:

1、内存缓存图片处理

使用NSMutableDictionary存储图片UIImage,数组的Key为该图片的URL地址
//缓存图片到内存上

[plain] view
plaincopy

[memCache setObject:image forKey:key];

2、物理缓存图片处理

把图片保持到物理存储设备上,则直接使用NSFileManager,把URL作为文件名保存

3、网络图片下载处理

图片使用异步下载,下载完后把图片保持到NSMutableDictionary和物理存储上

以下是摘自SDWebImageleik网络图片缓存处理的一个类,有详细注释

.h文件

[plain] view
plaincopy

@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文件

[plain] view
plaincopy

@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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: