IOS网络图片缓存详解

来源:互联网 发布:卡通农场mac版 编辑:程序博客网 时间:2024/05/01 01:54

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

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


处理网络图片缓存步骤:

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

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

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

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


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

1、内存缓存图片处理

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

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


2、物理缓存图片处理

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


3、网络图片下载处理

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


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

.h文件

[plain] view plaincopy
  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 plaincopy
  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  
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 华为麦芒54g信号差怎么办 华为手机wifi信号差怎么办 华为手机导航信号弱怎么办 华为手机gps信号弱怎么办 麦芒5指纹消失了怎么办 华为应用锁密码忘了怎么办 华为麦芒5密码忘了怎么办 华为卡1无服务怎么办 经常卡1无服务怎么办 华为手机进水无服务怎么办 苹果手机进水后无服务怎么办 苹果6进水无服务怎么办 华为手机突然无服务怎么办 sim卡显示无服务怎么办 华为麦芒进水无限开关机怎么办 华为麦芒5进水黑屏怎么办 华为麦芒6进水了怎么办 4g手机开不开机怎么办 全屏钢化膜总是翘边怎么办 华为麦芒屏幕触屏失灵怎么办 华为麦芒5运行慢怎么办 手机屏保密码忘记了怎么办 麦芒5密码锁忘了怎么办 超薄手机壳松了怎么办 华为麦芒5声音小怎么办 笔记本外壳a面裂了怎么办 苹果手机外壳摔坏了怎么办 挂衣服肩膀出包怎么办 摩拜单车手机号注销了怎么办 摩拜单车手机号码换了怎么办 摩拜单车换手机号码打不开怎么办 摩拜单车丢了怎么办 摩拜单车忘锁了怎么办 透明手机壳粘指纹怎么办 tpu手机壳变黄了怎么办 0pp0手机声音小怎么办 橡胶皮套晒坏了怎么办 宝宝晚上吹空调发烧怎么办 玩手机手指尖疼怎么办 手机型号不支持微信运动怎么办 手腕向下压会疼怎么办