SDImageCache.m 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  1. /*
  2. * This file is part of the SDWebImage package.
  3. * (c) Olivier Poitrey <rs@dailymotion.com>
  4. *
  5. * For the full copyright and license information, please view the LICENSE
  6. * file that was distributed with this source code.
  7. */
  8. #import "SDImageCache.h"
  9. #import "SDMemoryCache.h"
  10. #import "SDDiskCache.h"
  11. #import "NSImage+Compatibility.h"
  12. #import "SDImageCodersManager.h"
  13. #import "SDImageTransformer.h"
  14. #import "SDImageCoderHelper.h"
  15. #import "SDAnimatedImage.h"
  16. #import "UIImage+MemoryCacheCost.h"
  17. #import "UIImage+Metadata.h"
  18. @interface SDImageCache ()
  19. #pragma mark - Properties
  20. @property (nonatomic, strong, nonnull) id<SDMemoryCache> memCache;
  21. @property (nonatomic, strong, nonnull) id<SDDiskCache> diskCache;
  22. @property (nonatomic, copy, readwrite, nonnull) SDImageCacheConfig *config;
  23. @property (nonatomic, copy, readwrite, nonnull) NSString *diskCachePath;
  24. @property (nonatomic, strong, nullable) dispatch_queue_t ioQueue;
  25. @end
  26. @implementation SDImageCache
  27. #pragma mark - Singleton, init, dealloc
  28. + (nonnull instancetype)sharedImageCache {
  29. static dispatch_once_t once;
  30. static id instance;
  31. dispatch_once(&once, ^{
  32. instance = [self new];
  33. });
  34. return instance;
  35. }
  36. - (instancetype)init {
  37. return [self initWithNamespace:@"default"];
  38. }
  39. - (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns {
  40. return [self initWithNamespace:ns diskCacheDirectory:nil];
  41. }
  42. - (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
  43. diskCacheDirectory:(nullable NSString *)directory {
  44. return [self initWithNamespace:ns diskCacheDirectory:directory config:SDImageCacheConfig.defaultCacheConfig];
  45. }
  46. - (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
  47. diskCacheDirectory:(nullable NSString *)directory
  48. config:(nullable SDImageCacheConfig *)config {
  49. if ((self = [super init])) {
  50. NSAssert(ns, @"Cache namespace should not be nil");
  51. // Create IO serial queue
  52. _ioQueue = dispatch_queue_create("com.hackemist.SDImageCache", DISPATCH_QUEUE_SERIAL);
  53. if (!config) {
  54. config = SDImageCacheConfig.defaultCacheConfig;
  55. }
  56. _config = [config copy];
  57. // Init the memory cache
  58. NSAssert([config.memoryCacheClass conformsToProtocol:@protocol(SDMemoryCache)], @"Custom memory cache class must conform to `SDMemoryCache` protocol");
  59. _memCache = [[config.memoryCacheClass alloc] initWithConfig:_config];
  60. // Init the disk cache
  61. if (directory != nil) {
  62. _diskCachePath = [directory stringByAppendingPathComponent:ns];
  63. } else {
  64. NSString *path = [[[self userCacheDirectory] stringByAppendingPathComponent:@"com.hackemist.SDImageCache"] stringByAppendingPathComponent:ns];
  65. _diskCachePath = path;
  66. }
  67. NSAssert([config.diskCacheClass conformsToProtocol:@protocol(SDDiskCache)], @"Custom disk cache class must conform to `SDDiskCache` protocol");
  68. _diskCache = [[config.diskCacheClass alloc] initWithCachePath:_diskCachePath config:_config];
  69. // Check and migrate disk cache directory if need
  70. [self migrateDiskCacheDirectory];
  71. #if SD_UIKIT
  72. // Subscribe to app events
  73. [[NSNotificationCenter defaultCenter] addObserver:self
  74. selector:@selector(applicationWillTerminate:)
  75. name:UIApplicationWillTerminateNotification
  76. object:nil];
  77. [[NSNotificationCenter defaultCenter] addObserver:self
  78. selector:@selector(applicationDidEnterBackground:)
  79. name:UIApplicationDidEnterBackgroundNotification
  80. object:nil];
  81. #endif
  82. #if SD_MAC
  83. [[NSNotificationCenter defaultCenter] addObserver:self
  84. selector:@selector(applicationWillTerminate:)
  85. name:NSApplicationWillTerminateNotification
  86. object:nil];
  87. #endif
  88. }
  89. return self;
  90. }
  91. - (void)dealloc {
  92. [[NSNotificationCenter defaultCenter] removeObserver:self];
  93. }
  94. #pragma mark - Cache paths
  95. - (nullable NSString *)cachePathForKey:(nullable NSString *)key {
  96. if (!key) {
  97. return nil;
  98. }
  99. return [self.diskCache cachePathForKey:key];
  100. }
  101. - (nullable NSString *)userCacheDirectory {
  102. NSArray<NSString *> *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
  103. return paths.firstObject;
  104. }
  105. - (void)migrateDiskCacheDirectory {
  106. if ([self.diskCache isKindOfClass:[SDDiskCache class]]) {
  107. static dispatch_once_t onceToken;
  108. dispatch_once(&onceToken, ^{
  109. // ~/Library/Caches/com.hackemist.SDImageCache/default/
  110. NSString *newDefaultPath = [[[self userCacheDirectory] stringByAppendingPathComponent:@"com.hackemist.SDImageCache"] stringByAppendingPathComponent:@"default"];
  111. // ~/Library/Caches/default/com.hackemist.SDWebImageCache.default/
  112. NSString *oldDefaultPath = [[[self userCacheDirectory] stringByAppendingPathComponent:@"default"] stringByAppendingPathComponent:@"com.hackemist.SDWebImageCache.default"];
  113. dispatch_async(self.ioQueue, ^{
  114. [((SDDiskCache *)self.diskCache) moveCacheDirectoryFromPath:oldDefaultPath toPath:newDefaultPath];
  115. });
  116. });
  117. }
  118. }
  119. #pragma mark - Store Ops
  120. - (void)storeImage:(nullable UIImage *)image
  121. forKey:(nullable NSString *)key
  122. completion:(nullable SDWebImageNoParamsBlock)completionBlock {
  123. [self storeImage:image imageData:nil forKey:key toDisk:YES completion:completionBlock];
  124. }
  125. - (void)storeImage:(nullable UIImage *)image
  126. forKey:(nullable NSString *)key
  127. toDisk:(BOOL)toDisk
  128. completion:(nullable SDWebImageNoParamsBlock)completionBlock {
  129. [self storeImage:image imageData:nil forKey:key toDisk:toDisk completion:completionBlock];
  130. }
  131. - (void)storeImage:(nullable UIImage *)image
  132. imageData:(nullable NSData *)imageData
  133. forKey:(nullable NSString *)key
  134. toDisk:(BOOL)toDisk
  135. completion:(nullable SDWebImageNoParamsBlock)completionBlock {
  136. return [self storeImage:image imageData:imageData forKey:key toMemory:YES toDisk:toDisk completion:completionBlock];
  137. }
  138. - (void)storeImage:(nullable UIImage *)image
  139. imageData:(nullable NSData *)imageData
  140. forKey:(nullable NSString *)key
  141. toMemory:(BOOL)toMemory
  142. toDisk:(BOOL)toDisk
  143. completion:(nullable SDWebImageNoParamsBlock)completionBlock {
  144. if (!image || !key) {
  145. if (completionBlock) {
  146. completionBlock();
  147. }
  148. return;
  149. }
  150. // if memory cache is enabled
  151. if (toMemory && self.config.shouldCacheImagesInMemory) {
  152. NSUInteger cost = image.sd_memoryCost;
  153. [self.memCache setObject:image forKey:key cost:cost];
  154. }
  155. if (toDisk) {
  156. dispatch_async(self.ioQueue, ^{
  157. @autoreleasepool {
  158. NSData *data = imageData;
  159. if (!data && image) {
  160. // If we do not have any data to detect image format, check whether it contains alpha channel to use PNG or JPEG format
  161. SDImageFormat format;
  162. if ([SDImageCoderHelper CGImageContainsAlpha:image.CGImage]) {
  163. format = SDImageFormatPNG;
  164. } else {
  165. format = SDImageFormatJPEG;
  166. }
  167. data = [[SDImageCodersManager sharedManager] encodedDataWithImage:image format:format options:nil];
  168. }
  169. [self _storeImageDataToDisk:data forKey:key];
  170. }
  171. if (completionBlock) {
  172. dispatch_async(dispatch_get_main_queue(), ^{
  173. completionBlock();
  174. });
  175. }
  176. });
  177. } else {
  178. if (completionBlock) {
  179. completionBlock();
  180. }
  181. }
  182. }
  183. - (void)storeImageToMemory:(UIImage *)image forKey:(NSString *)key {
  184. if (!image || !key) {
  185. return;
  186. }
  187. NSUInteger cost = image.sd_memoryCost;
  188. [self.memCache setObject:image forKey:key cost:cost];
  189. }
  190. - (void)storeImageDataToDisk:(nullable NSData *)imageData
  191. forKey:(nullable NSString *)key {
  192. if (!imageData || !key) {
  193. return;
  194. }
  195. dispatch_sync(self.ioQueue, ^{
  196. [self _storeImageDataToDisk:imageData forKey:key];
  197. });
  198. }
  199. // Make sure to call form io queue by caller
  200. - (void)_storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key {
  201. if (!imageData || !key) {
  202. return;
  203. }
  204. [self.diskCache setData:imageData forKey:key];
  205. }
  206. #pragma mark - Query and Retrieve Ops
  207. - (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDImageCacheCheckCompletionBlock)completionBlock {
  208. dispatch_async(self.ioQueue, ^{
  209. BOOL exists = [self _diskImageDataExistsWithKey:key];
  210. if (completionBlock) {
  211. dispatch_async(dispatch_get_main_queue(), ^{
  212. completionBlock(exists);
  213. });
  214. }
  215. });
  216. }
  217. - (BOOL)diskImageDataExistsWithKey:(nullable NSString *)key {
  218. if (!key) {
  219. return NO;
  220. }
  221. __block BOOL exists = NO;
  222. dispatch_sync(self.ioQueue, ^{
  223. exists = [self _diskImageDataExistsWithKey:key];
  224. });
  225. return exists;
  226. }
  227. // Make sure to call form io queue by caller
  228. - (BOOL)_diskImageDataExistsWithKey:(nullable NSString *)key {
  229. if (!key) {
  230. return NO;
  231. }
  232. return [self.diskCache containsDataForKey:key];
  233. }
  234. - (nullable NSData *)diskImageDataForKey:(nullable NSString *)key {
  235. if (!key) {
  236. return nil;
  237. }
  238. __block NSData *imageData = nil;
  239. dispatch_sync(self.ioQueue, ^{
  240. imageData = [self diskImageDataBySearchingAllPathsForKey:key];
  241. });
  242. return imageData;
  243. }
  244. - (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key {
  245. return [self.memCache objectForKey:key];
  246. }
  247. - (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key {
  248. UIImage *diskImage = [self diskImageForKey:key];
  249. if (diskImage && self.config.shouldCacheImagesInMemory) {
  250. NSUInteger cost = diskImage.sd_memoryCost;
  251. [self.memCache setObject:diskImage forKey:key cost:cost];
  252. }
  253. return diskImage;
  254. }
  255. - (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key {
  256. // First check the in-memory cache...
  257. UIImage *image = [self imageFromMemoryCacheForKey:key];
  258. if (image) {
  259. return image;
  260. }
  261. // Second check the disk cache...
  262. image = [self imageFromDiskCacheForKey:key];
  263. return image;
  264. }
  265. - (nullable NSData *)diskImageDataBySearchingAllPathsForKey:(nullable NSString *)key {
  266. if (!key) {
  267. return nil;
  268. }
  269. NSData *data = [self.diskCache dataForKey:key];
  270. if (data) {
  271. return data;
  272. }
  273. // Addtional cache path for custom pre-load cache
  274. if (self.additionalCachePathBlock) {
  275. NSString *filePath = self.additionalCachePathBlock(key);
  276. if (filePath) {
  277. data = [NSData dataWithContentsOfFile:filePath options:self.config.diskCacheReadingOptions error:nil];
  278. }
  279. }
  280. return data;
  281. }
  282. - (nullable UIImage *)diskImageForKey:(nullable NSString *)key {
  283. NSData *data = [self diskImageDataForKey:key];
  284. return [self diskImageForKey:key data:data];
  285. }
  286. - (nullable UIImage *)diskImageForKey:(nullable NSString *)key data:(nullable NSData *)data {
  287. return [self diskImageForKey:key data:data options:0 context:nil];
  288. }
  289. - (nullable UIImage *)diskImageForKey:(nullable NSString *)key data:(nullable NSData *)data options:(SDImageCacheOptions)options context:(SDWebImageContext *)context {
  290. if (data) {
  291. UIImage *image = SDImageCacheDecodeImageData(data, key, [[self class] imageOptionsFromCacheOptions:options], context);
  292. return image;
  293. } else {
  294. return nil;
  295. }
  296. }
  297. - (nullable NSOperation *)queryCacheOperationForKey:(NSString *)key done:(SDImageCacheQueryCompletionBlock)doneBlock {
  298. return [self queryCacheOperationForKey:key options:0 done:doneBlock];
  299. }
  300. - (nullable NSOperation *)queryCacheOperationForKey:(NSString *)key options:(SDImageCacheOptions)options done:(SDImageCacheQueryCompletionBlock)doneBlock {
  301. return [self queryCacheOperationForKey:key options:options context:nil done:doneBlock];
  302. }
  303. - (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options context:(nullable SDWebImageContext *)context done:(nullable SDImageCacheQueryCompletionBlock)doneBlock {
  304. if (!key) {
  305. if (doneBlock) {
  306. doneBlock(nil, nil, SDImageCacheTypeNone);
  307. }
  308. return nil;
  309. }
  310. id<SDImageTransformer> transformer = context[SDWebImageContextImageTransformer];
  311. if (transformer) {
  312. // grab the transformed disk image if transformer provided
  313. NSString *transformerKey = [transformer transformerKey];
  314. key = SDTransformedKeyForKey(key, transformerKey);
  315. }
  316. // First check the in-memory cache...
  317. UIImage *image = [self imageFromMemoryCacheForKey:key];
  318. if ((options & SDImageCacheDecodeFirstFrameOnly) && image.sd_isAnimated) {
  319. #if SD_MAC
  320. image = [[NSImage alloc] initWithCGImage:image.CGImage scale:image.scale orientation:kCGImagePropertyOrientationUp];
  321. #else
  322. image = [[UIImage alloc] initWithCGImage:image.CGImage scale:image.scale orientation:image.imageOrientation];
  323. #endif
  324. }
  325. BOOL shouldQueryMemoryOnly = (image && !(options & SDImageCacheQueryMemoryData));
  326. if (shouldQueryMemoryOnly) {
  327. if (doneBlock) {
  328. doneBlock(image, nil, SDImageCacheTypeMemory);
  329. }
  330. return nil;
  331. }
  332. // Second check the disk cache...
  333. NSOperation *operation = [NSOperation new];
  334. // Check whether we need to synchronously query disk
  335. // 1. in-memory cache hit & memoryDataSync
  336. // 2. in-memory cache miss & diskDataSync
  337. BOOL shouldQueryDiskSync = ((image && options & SDImageCacheQueryMemoryDataSync) ||
  338. (!image && options & SDImageCacheQueryDiskDataSync));
  339. void(^queryDiskBlock)(void) = ^{
  340. if (operation.isCancelled) {
  341. // do not call the completion if cancelled
  342. return;
  343. }
  344. @autoreleasepool {
  345. NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
  346. UIImage *diskImage;
  347. SDImageCacheType cacheType = SDImageCacheTypeNone;
  348. if (image) {
  349. // the image is from in-memory cache, but need image data
  350. diskImage = image;
  351. cacheType = SDImageCacheTypeMemory;
  352. } else if (diskData) {
  353. cacheType = SDImageCacheTypeDisk;
  354. // decode image data only if in-memory cache missed
  355. diskImage = [self diskImageForKey:key data:diskData options:options context:context];
  356. if (diskImage && self.config.shouldCacheImagesInMemory) {
  357. NSUInteger cost = diskImage.sd_memoryCost;
  358. [self.memCache setObject:diskImage forKey:key cost:cost];
  359. }
  360. }
  361. if (doneBlock) {
  362. if (shouldQueryDiskSync) {
  363. doneBlock(diskImage, diskData, cacheType);
  364. } else {
  365. dispatch_async(dispatch_get_main_queue(), ^{
  366. doneBlock(diskImage, diskData, cacheType);
  367. });
  368. }
  369. }
  370. }
  371. };
  372. // Query in ioQueue to keep IO-safe
  373. if (shouldQueryDiskSync) {
  374. dispatch_sync(self.ioQueue, queryDiskBlock);
  375. } else {
  376. dispatch_async(self.ioQueue, queryDiskBlock);
  377. }
  378. return operation;
  379. }
  380. #pragma mark - Remove Ops
  381. - (void)removeImageForKey:(nullable NSString *)key withCompletion:(nullable SDWebImageNoParamsBlock)completion {
  382. [self removeImageForKey:key fromDisk:YES withCompletion:completion];
  383. }
  384. - (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion {
  385. [self removeImageForKey:key fromMemory:YES fromDisk:fromDisk withCompletion:completion];
  386. }
  387. - (void)removeImageForKey:(nullable NSString *)key fromMemory:(BOOL)fromMemory fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion {
  388. if (key == nil) {
  389. return;
  390. }
  391. if (fromMemory && self.config.shouldCacheImagesInMemory) {
  392. [self.memCache removeObjectForKey:key];
  393. }
  394. if (fromDisk) {
  395. dispatch_async(self.ioQueue, ^{
  396. [self.diskCache removeDataForKey:key];
  397. if (completion) {
  398. dispatch_async(dispatch_get_main_queue(), ^{
  399. completion();
  400. });
  401. }
  402. });
  403. } else if (completion) {
  404. completion();
  405. }
  406. }
  407. - (void)removeImageFromMemoryForKey:(NSString *)key {
  408. if (!key) {
  409. return;
  410. }
  411. [self.memCache removeObjectForKey:key];
  412. }
  413. - (void)removeImageFromDiskForKey:(NSString *)key {
  414. if (!key) {
  415. return;
  416. }
  417. dispatch_sync(self.ioQueue, ^{
  418. [self _removeImageFromDiskForKey:key];
  419. });
  420. }
  421. // Make sure to call form io queue by caller
  422. - (void)_removeImageFromDiskForKey:(NSString *)key {
  423. if (!key) {
  424. return;
  425. }
  426. [self.diskCache removeDataForKey:key];
  427. }
  428. #pragma mark - Cache clean Ops
  429. - (void)clearMemory {
  430. [self.memCache removeAllObjects];
  431. }
  432. - (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion {
  433. dispatch_async(self.ioQueue, ^{
  434. [self.diskCache removeAllData];
  435. if (completion) {
  436. dispatch_async(dispatch_get_main_queue(), ^{
  437. completion();
  438. });
  439. }
  440. });
  441. }
  442. - (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock {
  443. dispatch_async(self.ioQueue, ^{
  444. [self.diskCache removeExpiredData];
  445. if (completionBlock) {
  446. dispatch_async(dispatch_get_main_queue(), ^{
  447. completionBlock();
  448. });
  449. }
  450. });
  451. }
  452. #pragma mark - UIApplicationWillTerminateNotification
  453. #if SD_UIKIT || SD_MAC
  454. - (void)applicationWillTerminate:(NSNotification *)notification {
  455. [self deleteOldFilesWithCompletionBlock:nil];
  456. }
  457. #endif
  458. #pragma mark - UIApplicationDidEnterBackgroundNotification
  459. #if SD_UIKIT
  460. - (void)applicationDidEnterBackground:(NSNotification *)notification {
  461. if (!self.config.shouldRemoveExpiredDataWhenEnterBackground) {
  462. return;
  463. }
  464. Class UIApplicationClass = NSClassFromString(@"UIApplication");
  465. if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
  466. return;
  467. }
  468. UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)];
  469. __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
  470. // Clean up any unfinished task business by marking where you
  471. // stopped or ending the task outright.
  472. [application endBackgroundTask:bgTask];
  473. bgTask = UIBackgroundTaskInvalid;
  474. }];
  475. // Start the long-running task and return immediately.
  476. [self deleteOldFilesWithCompletionBlock:^{
  477. [application endBackgroundTask:bgTask];
  478. bgTask = UIBackgroundTaskInvalid;
  479. }];
  480. }
  481. #endif
  482. #pragma mark - Cache Info
  483. - (NSUInteger)totalDiskSize {
  484. __block NSUInteger size = 0;
  485. dispatch_sync(self.ioQueue, ^{
  486. size = [self.diskCache totalSize];
  487. });
  488. return size;
  489. }
  490. - (NSUInteger)totalDiskCount {
  491. __block NSUInteger count = 0;
  492. dispatch_sync(self.ioQueue, ^{
  493. count = [self.diskCache totalCount];
  494. });
  495. return count;
  496. }
  497. - (void)calculateSizeWithCompletionBlock:(nullable SDImageCacheCalculateSizeBlock)completionBlock {
  498. dispatch_async(self.ioQueue, ^{
  499. NSUInteger fileCount = [self.diskCache totalCount];
  500. NSUInteger totalSize = [self.diskCache totalSize];
  501. if (completionBlock) {
  502. dispatch_async(dispatch_get_main_queue(), ^{
  503. completionBlock(fileCount, totalSize);
  504. });
  505. }
  506. });
  507. }
  508. #pragma mark - Helper
  509. + (SDWebImageOptions)imageOptionsFromCacheOptions:(SDImageCacheOptions)cacheOptions {
  510. SDWebImageOptions options = 0;
  511. if (cacheOptions & SDImageCacheScaleDownLargeImages) options |= SDWebImageScaleDownLargeImages;
  512. if (cacheOptions & SDImageCacheDecodeFirstFrameOnly) options |= SDWebImageDecodeFirstFrameOnly;
  513. if (cacheOptions & SDImageCachePreloadAllFrames) options |= SDWebImagePreloadAllFrames;
  514. if (cacheOptions & SDImageCacheAvoidDecodeImage) options |= SDWebImageAvoidDecodeImage;
  515. return options;
  516. }
  517. @end
  518. @implementation SDImageCache (SDImageCache)
  519. #pragma mark - SDImageCache
  520. - (id<SDWebImageOperation>)queryImageForKey:(NSString *)key options:(SDWebImageOptions)options context:(nullable SDWebImageContext *)context completion:(nullable SDImageCacheQueryCompletionBlock)completionBlock {
  521. SDImageCacheOptions cacheOptions = 0;
  522. if (options & SDWebImageQueryMemoryData) cacheOptions |= SDImageCacheQueryMemoryData;
  523. if (options & SDWebImageQueryMemoryDataSync) cacheOptions |= SDImageCacheQueryMemoryDataSync;
  524. if (options & SDWebImageQueryDiskDataSync) cacheOptions |= SDImageCacheQueryDiskDataSync;
  525. if (options & SDWebImageScaleDownLargeImages) cacheOptions |= SDImageCacheScaleDownLargeImages;
  526. if (options & SDWebImageAvoidDecodeImage) cacheOptions |= SDImageCacheAvoidDecodeImage;
  527. if (options & SDWebImageDecodeFirstFrameOnly) cacheOptions |= SDImageCacheDecodeFirstFrameOnly;
  528. if (options & SDWebImagePreloadAllFrames) cacheOptions |= SDImageCachePreloadAllFrames;
  529. return [self queryCacheOperationForKey:key options:cacheOptions context:context done:completionBlock];
  530. }
  531. - (void)storeImage:(UIImage *)image imageData:(NSData *)imageData forKey:(nullable NSString *)key cacheType:(SDImageCacheType)cacheType completion:(nullable SDWebImageNoParamsBlock)completionBlock {
  532. switch (cacheType) {
  533. case SDImageCacheTypeNone: {
  534. [self storeImage:image imageData:imageData forKey:key toMemory:NO toDisk:NO completion:completionBlock];
  535. }
  536. break;
  537. case SDImageCacheTypeMemory: {
  538. [self storeImage:image imageData:imageData forKey:key toMemory:YES toDisk:NO completion:completionBlock];
  539. }
  540. break;
  541. case SDImageCacheTypeDisk: {
  542. [self storeImage:image imageData:imageData forKey:key toMemory:NO toDisk:YES completion:completionBlock];
  543. }
  544. break;
  545. case SDImageCacheTypeAll: {
  546. [self storeImage:image imageData:imageData forKey:key toMemory:YES toDisk:YES completion:completionBlock];
  547. }
  548. break;
  549. default: {
  550. if (completionBlock) {
  551. completionBlock();
  552. }
  553. }
  554. break;
  555. }
  556. }
  557. - (void)removeImageForKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(nullable SDWebImageNoParamsBlock)completionBlock {
  558. switch (cacheType) {
  559. case SDImageCacheTypeNone: {
  560. [self removeImageForKey:key fromMemory:NO fromDisk:NO withCompletion:completionBlock];
  561. }
  562. break;
  563. case SDImageCacheTypeMemory: {
  564. [self removeImageForKey:key fromMemory:YES fromDisk:NO withCompletion:completionBlock];
  565. }
  566. break;
  567. case SDImageCacheTypeDisk: {
  568. [self removeImageForKey:key fromMemory:NO fromDisk:YES withCompletion:completionBlock];
  569. }
  570. break;
  571. case SDImageCacheTypeAll: {
  572. [self removeImageForKey:key fromMemory:YES fromDisk:YES withCompletion:completionBlock];
  573. }
  574. break;
  575. default: {
  576. if (completionBlock) {
  577. completionBlock();
  578. }
  579. }
  580. break;
  581. }
  582. }
  583. - (void)containsImageForKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(nullable SDImageCacheContainsCompletionBlock)completionBlock {
  584. switch (cacheType) {
  585. case SDImageCacheTypeNone: {
  586. if (completionBlock) {
  587. completionBlock(SDImageCacheTypeNone);
  588. }
  589. }
  590. break;
  591. case SDImageCacheTypeMemory: {
  592. BOOL isInMemoryCache = ([self imageFromMemoryCacheForKey:key] != nil);
  593. if (completionBlock) {
  594. completionBlock(isInMemoryCache ? SDImageCacheTypeMemory : SDImageCacheTypeNone);
  595. }
  596. }
  597. break;
  598. case SDImageCacheTypeDisk: {
  599. [self diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) {
  600. if (completionBlock) {
  601. completionBlock(isInDiskCache ? SDImageCacheTypeDisk : SDImageCacheTypeNone);
  602. }
  603. }];
  604. }
  605. break;
  606. case SDImageCacheTypeAll: {
  607. BOOL isInMemoryCache = ([self imageFromMemoryCacheForKey:key] != nil);
  608. if (isInMemoryCache) {
  609. if (completionBlock) {
  610. completionBlock(SDImageCacheTypeMemory);
  611. }
  612. return;
  613. }
  614. [self diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) {
  615. if (completionBlock) {
  616. completionBlock(isInDiskCache ? SDImageCacheTypeDisk : SDImageCacheTypeNone);
  617. }
  618. }];
  619. }
  620. break;
  621. default:
  622. if (completionBlock) {
  623. completionBlock(SDImageCacheTypeNone);
  624. }
  625. break;
  626. }
  627. }
  628. - (void)clearWithCacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock {
  629. switch (cacheType) {
  630. case SDImageCacheTypeNone: {
  631. if (completionBlock) {
  632. completionBlock();
  633. }
  634. }
  635. break;
  636. case SDImageCacheTypeMemory: {
  637. [self clearMemory];
  638. if (completionBlock) {
  639. completionBlock();
  640. }
  641. }
  642. break;
  643. case SDImageCacheTypeDisk: {
  644. [self clearDiskOnCompletion:completionBlock];
  645. }
  646. break;
  647. case SDImageCacheTypeAll: {
  648. [self clearMemory];
  649. [self clearDiskOnCompletion:completionBlock];
  650. }
  651. break;
  652. default: {
  653. if (completionBlock) {
  654. completionBlock();
  655. }
  656. }
  657. break;
  658. }
  659. }
  660. @end