IOS網絡圖片緩存詳解 – iPhone手機開發技術文章 iPhone軟體開發教學課程

IOS網絡圖片緩存詳

 

 

 

 

 

在開發移動應用的時候比如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 )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 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 )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

You May Also Like