IOS网络图片缓存详解

IOS网络图片缓存详




官方对网络缓存的解析

http://www.cocoachina.com/newbie/basic/2012/0118/3893.html



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

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


处理网络图片缓存步骤:

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

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

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

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


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

1、内存缓存图片处理

使用NSMutableDictionary存储图片UIImage,数组的Key为该图片的URL地址

//缓存图片到内存上
[plain]  view plain copy
  1. [memCache setObject:image forKey:key];  


2、物理缓存图片处理

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


3、网络图片下载处理

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


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

.h文件

[plain]  view plain copy
  1. @interface SDImageCache : NSObject  
  2. {  
  3.     NSMutableDictionary *memCache;//内存缓存图片引用  
  4.     NSString *diskCachePath;//物理缓存路径  
  5.     NSOperationQueue *cacheInQueue, *cacheOutQueue;  
  6. }  
  7.   
  8. + (SDImageCache *)sharedImageCache;  
  9.   
  10. //保存图片  
  11. - (void)storeImage:(UIImage *)image forKey:(NSString *)key;  
  12.   
  13. //保存图片,并选择是否保存到物理存储上  
  14. - (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk;  
  15.   
  16. //保存图片,可以选择把NSData数据保存到物理存储上  
  17. - (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)key toDisk:(BOOL)toDisk;  
  18.   
  19. //通过key返回UIImage  
  20. - (UIImage *)imageFromKey:(NSString *)key;  
  21.   
  22. //如果获取内存图片失败,是否可以在物理存储上查找  
  23. - (UIImage *)imageFromKey:(NSString *)key fromDisk:(BOOL)fromDisk;  
  24.   
  25.   
  26. - (void)queryDiskCacheForKey:(NSString *)key delegate:(id <SDImageCacheDelegate>)delegate userInfo:(NSDictionary *)info;  
  27.   
  28. //清除key索引的图片  
  29. - (void)removeImageForKey:(NSString *)key;  
  30. //清除内存图片  
  31. - (void)clearMemory;  
  32. //清除物理缓存  
  33. - (void)clearDisk;  
  34. //清除过期物理缓存  
  35. - (void)cleanDisk;  
  36.   
  37. @end  

.m文件
[plain]  view plain copy
  1. @implementation SDImageCache  
  2.   
  3. #pragma mark NSObject  
  4.   
  5. - (id)init  
  6. {  
  7.     if ((self = [super init]))  
  8.     {  
  9.         // Init the memory cache  
  10.         memCache = [[NSMutableDictionary alloc] init];  
  11.           
  12.         // Init the disk cache  
  13.         NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);  
  14.         diskCachePath = [[[paths objectAtIndex:0] stringByAppendingPathComponent:@"ImageCache"] retain];  
  15.           
  16.         if (![[NSFileManager defaultManager] fileExistsAtPath:diskCachePath])  
  17.         {  
  18.             [[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath  
  19.                                       withIntermediateDirectories:YES  
  20.                                                        attributes:nil  
  21.                                                             error:NULL];  
  22.         }  
  23.           
  24.         // Init the operation queue  
  25.         cacheInQueue = [[NSOperationQueue alloc] init];  
  26.         cacheInQueue.maxConcurrentOperationCount = 1;  
  27.         cacheOutQueue = [[NSOperationQueue alloc] init];  
  28.         cacheOutQueue.maxConcurrentOperationCount = 1;  
  29.           
  30. #if TARGET_OS_IPHONE  
  31.         // Subscribe to app events  
  32.         [[NSNotificationCenter defaultCenter] addObserver:self  
  33.                                                  selector:@selector(clearMemory)  
  34.                                                      name:UIApplicationDidReceiveMemoryWarningNotification  
  35.                                                    object:nil];  
  36.           
  37.         [[NSNotificationCenter defaultCenter] addObserver:self  
  38.                                                  selector:@selector(cleanDisk)  
  39.                                                      name:UIApplicationWillTerminateNotification  
  40.                                                    object:nil];  
  41.           
  42. #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_0  
  43.         UIDevice *device = [UIDevice currentDevice];  
  44.         if ([device respondsToSelector:@selector(isMultitaskingSupported)] && device.multitaskingSupported)  
  45.         {  
  46.             // When in background, clean memory in order to have less chance to be killed  
  47.             [[NSNotificationCenter defaultCenter] addObserver:self  
  48.                                                      selector:@selector(clearMemory)  
  49.                                                          name:UIApplicationDidEnterBackgroundNotification  
  50.                                                        object:nil];  
  51.         }  
  52. #endif  
  53. #endif  
  54.     }  
  55.       
  56.     return self;  
  57. }  
  58.   
  59. - (void)dealloc  
  60. {  
  61.     [memCache release], memCache = nil;  
  62.     [diskCachePath release], diskCachePath = nil;  
  63.     [cacheInQueue release], cacheInQueue = nil;  
  64.       
  65.     [[NSNotificationCenter defaultCenter] removeObserver:self];  
  66.       
  67.     [super dealloc];  
  68. }  
  69.   
  70. #pragma mark SDImageCache (class methods)  
  71.   
  72. + (SDImageCache *)sharedImageCache  
  73. {  
  74.     if (instance == nil)  
  75.     {  
  76.         instance = [[SDImageCache alloc] init];  
  77.     }  
  78.       
  79.     return instance;  
  80. }  
  81.   
  82. #pragma mark SDImageCache (private)  
  83.   
  84. /*  
  85.  *创建指定图片key的路径  
  86.  */  
  87. - (NSString *)cachePathForKey:(NSString *)key  
  88. {  
  89.     const char *str = [key UTF8String];  
  90.     unsigned char r[CC_MD5_DIGEST_LENGTH];  
  91.     CC_MD5(str, (CC_LONG)strlen(str), r);  
  92.     NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",  
  93.                           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]];  
  94.       
  95.     return [diskCachePath stringByAppendingPathComponent:filename];  
  96. }  
  97.   
  98. /*  
  99.  *保存key和Data到物理存储  
  100.  *keyAndData[0] ->key  
  101.  *keyAndData[1] ->Data  
  102.  */  
  103. - (void)storeKeyWithDataToDisk:(NSArray *)keyAndData  
  104. {  
  105.     // Can't use defaultManager another thread  
  106.     NSFileManager *fileManager = [[NSFileManager alloc] init];  
  107.       
  108.     NSString *key = [keyAndData objectAtIndex:0];  
  109.     NSData *data = [keyAndData count] > 1 ? [keyAndData objectAtIndex:1] : nil;  
  110.       
  111.     //如果有数据,则保存到物理存储上  
  112.     if (data)  
  113.     {  
  114.         [fileManager createFileAtPath:[self cachePathForKey:key] contents:data attributes:nil];  
  115.     }  
  116.     else  
  117.     {  
  118.         //如果没有data,则把UIImage转换为JPEG,并保存到物理存储上  
  119.         // If no data representation given, convert the UIImage in JPEG and store it  
  120.         // This trick is more CPU/memory intensive and doesn't preserve alpha channel  
  121.         UIImage *image = [[self imageFromKey:key fromDisk:YES] retain]; // be thread safe with no lock  
  122.         if (image)  
  123.         {  
  124. #if TARGET_OS_IPHONE  
  125.             [fileManager createFileAtPath:[self cachePathForKey:key] contents:UIImageJPEGRepresentation(image, (CGFloat)1.0) attributes:nil];  
  126. #else  
  127.             NSArray*  representations  = [image representations];  
  128.             NSData* jpegData = [NSBitmapImageRep representationOfImageRepsInArray: representations usingType: NSJPEGFileType properties:nil];  
  129.             [fileManager createFileAtPath:[self cachePathForKey:key] contents:jpegData attributes:nil];  
  130. #endif  
  131.             [image release];  
  132.         }  
  133.     }  
  134.       
  135.     [fileManager release];  
  136. }  
  137.   
  138. /*  
  139.  *查找图片委托  
  140.  */  
  141. - (void)notifyDelegate:(NSDictionary *)arguments  
  142. {  
  143.     NSString *key = [arguments objectForKey:@"key"];  
  144.     id <SDImageCacheDelegate> delegate = [arguments objectForKey:@"delegate"];  
  145.     NSDictionary *info = [arguments objectForKey:@"userInfo"];  
  146.     UIImage *image = [arguments objectForKey:@"image"];  
  147.       
  148.     if (image)  
  149.     {  
  150.         [memCache setObject:image forKey:key];  
  151.           
  152.         if ([delegate respondsToSelector:@selector(imageCache:didFindImage:forKey:userInfo:)])  
  153.         {  
  154.             [delegate imageCache:self didFindImage:image forKey:key userInfo:info];  
  155.         }  
  156.     }  
  157.     else  
  158.     {  
  159.         if ([delegate respondsToSelector:@selector(imageCache:didNotFindImageForKey:userInfo:)])  
  160.         {  
  161.             [delegate imageCache:self didNotFindImageForKey:key userInfo:info];  
  162.         }  
  163.     }  
  164. }  
  165.   
  166. /*  
  167.  *查找物理缓存上的图片  
  168.  */  
  169. - (void)queryDiskCacheOperation:(NSDictionary *)arguments  
  170. {  
  171.     NSString *key = [arguments objectForKey:@"key"];  
  172.     NSMutableDictionary *mutableArguments = [[arguments mutableCopy] autorelease];  
  173.       
  174.     UIImage *image = [[[UIImage alloc] initWithContentsOfFile:[self cachePathForKey:key]] autorelease];  
  175.     if (image)  
  176.     {  
  177. #ifdef ENABLE_SDWEBIMAGE_DECODER  
  178.         UIImage *decodedImage = [UIImage decodedImageWithImage:image];  
  179.         if (decodedImage)  
  180.         {  
  181.             image = decodedImage;  
  182.         }  
  183. #endif  
  184.         [mutableArguments setObject:image forKey:@"image"];  
  185.     }  
  186.       
  187.     [self performSelectorOnMainThread:@selector(notifyDelegate:) withObject:mutableArguments waitUntilDone:NO];  
  188. }  
  189.   
  190. #pragma mark ImageCache  
  191.   
  192. /*  
  193.  *缓存图片  
  194.  *  
  195.  **/  
  196. - (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)key toDisk:(BOOL)toDisk  
  197. {  
  198.     if (!image || !key)  
  199.     {  
  200.         return;  
  201.     }  
  202.       
  203.     //缓存图片到内存上  
  204.     [memCache setObject:image forKey:key];  
  205.       
  206.     //如果需要缓存到物理存储上,并data不为空,则把data缓存到物理存储上  
  207.     if (toDisk)  
  208.     {  
  209.         if (!data) return;  
  210.         NSArray *keyWithData;  
  211.         if (data)  
  212.         {  
  213.             keyWithData = [NSArray arrayWithObjects:key, data, nil];  
  214.         }  
  215.         else  
  216.         {  
  217.             keyWithData = [NSArray arrayWithObjects:key, nil];  
  218.         }  
  219.         //后台线程缓存图片到物理存储上  
  220.         [cacheInQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self  
  221.                                                                          selector:@selector(storeKeyWithDataToDisk:)  
  222.                                                                            object:keyWithData] autorelease]];  
  223.     }  
  224. }  
  225.   
  226. /*  
  227.  *保存图片到内存上,不保存到物理存储上  
  228.  */  
  229. - (void)storeImage:(UIImage *)image forKey:(NSString *)key  
  230. {  
  231.     [self storeImage:image imageData:nil forKey:key toDisk:YES];  
  232. }  
  233. /*  
  234.  *保存图片到内存上,不保存到物理存储上  
  235.  */  
  236. - (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk  
  237. {  
  238.     [self storeImage:image imageData:nil forKey:key toDisk:toDisk];  
  239. }  
  240.   
  241. /*  
  242.  *通过key返回指定图片  
  243.  */  
  244. - (UIImage *)imageFromKey:(NSString *)key  
  245. {  
  246.     return [self imageFromKey:key fromDisk:YES];  
  247. }  
  248.   
  249. /*  
  250.  *返回一张图像  
  251.  *key:图像的key  
  252.  *fromDisk:如果内存中没有图片,是否在物理存储上查找  
  253.  *return 返回查找到的图片,如果没有则返回nil  
  254.  */  
  255. - (UIImage *)imageFromKey:(NSString *)key fromDisk:(BOOL)fromDisk  
  256. {  
  257.     if (key == nil)  
  258.     {  
  259.         return nil;  
  260.     }  
  261.       
  262.     UIImage *image = [memCache objectForKey:key];  
  263.       
  264.     if (!image && fromDisk) //如果内存没有图片,并且可以在物理存储上查找,则返回物理存储上的图片  
  265.     {  
  266.         image = [[[UIImage alloc] initWithContentsOfFile:[self cachePathForKey:key]] autorelease];  
  267.         if (image)  
  268.         {  
  269.             [memCache setObject:image forKey:key];  
  270.         }  
  271.     }  
  272.       
  273.     return image;  
  274. }  
  275.   
  276. - (void)queryDiskCacheForKey:(NSString *)key delegate:(id <SDImageCacheDelegate>)delegate userInfo:(NSDictionary *)info  
  277. {  
  278.     if (!delegate)  
  279.     {  
  280.         return;  
  281.     }  
  282.       
  283.     if (!key)  
  284.     {  
  285.         if ([delegate respondsToSelector:@selector(imageCache:didNotFindImageForKey:userInfo:)])  
  286.         {  
  287.             [delegate imageCache:self didNotFindImageForKey:key userInfo:info];  
  288.         }  
  289.         return;  
  290.     }  
  291.       
  292.     // First check the in-memory cache...  
  293.     UIImage *image = [memCache objectForKey:key];  
  294.     if (image)  
  295.     {  
  296.         // ...notify delegate immediately, no need to go async  
  297.         if ([delegate respondsToSelector:@selector(imageCache:didFindImage:forKey:userInfo:)])  
  298.         {  
  299.             [delegate imageCache:self didFindImage:image forKey:key userInfo:info];  
  300.         }  
  301.         return;  
  302.     }  
  303.       
  304.     NSMutableDictionary *arguments = [NSMutableDictionary dictionaryWithCapacity:3];  
  305.     [arguments setObject:key forKey:@"key"];  
  306.     [arguments setObject:delegate forKey:@"delegate"];  
  307.     if (info)  
  308.     {  
  309.         [arguments setObject:info forKey:@"userInfo"];  
  310.     }  
  311.     [cacheOutQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(queryDiskCacheOperation:) object:arguments] autorelease]];  
  312. }  
  313.   
  314. /*  
  315.  *从内存和物理存储上移除指定图片  
  316.  */  
  317. - (void)removeImageForKey:(NSString *)key  
  318. {  
  319.     if (key == nil)  
  320.     {  
  321.         return;  
  322.     }  
  323.       
  324.     [memCache removeObjectForKey:key];  
  325.     [[NSFileManager defaultManager] removeItemAtPath:[self cachePathForKey:key] error:nil];  
  326. }  
  327. /*  
  328.  *清除内存缓存区的图片  
  329.  */  
  330. - (void)clearMemory  
  331. {  
  332.     [cacheInQueue cancelAllOperations]; // won't be able to complete  
  333.     [memCache removeAllObjects];  
  334. }  
  335.   
  336. /*  
  337.  *清除物理存储上的图片  
  338.  */  
  339. - (void)clearDisk  
  340. {  
  341.     [cacheInQueue cancelAllOperations];  
  342.     [[NSFileManager defaultManager] removeItemAtPath:diskCachePath error:nil];  
  343.     [[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath  
  344.                               withIntermediateDirectories:YES  
  345.                                                attributes:nil  
  346.                                                     error:NULL];  
  347. }  
  348. /*  
  349.  *清除过期缓存的图片  
  350.  */  
  351. - (void)cleanDisk  
  352. {  
  353.     NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-cacheMaxCacheAge];  
  354.     NSDirectoryEnumerator *fileEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:diskCachePath];  
  355.     for (NSString *fileName in fileEnumerator)  
  356.     {  
  357.         NSString *filePath = [diskCachePath stringByAppendingPathComponent:fileName];  
  358.         NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];  
  359.         if ([[[attrs fileModificationDate] laterDate:expirationDate] isEqualToDate:expirationDate])  
  360.         {  
  361.             [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];  
  362.         }  
  363.     }  
  364. }  
  365.   
  366. @end  

你可能感兴趣的:(ios,网络,缓存,移动应用)