SDImageCachesManager.m 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  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 "SDImageCachesManager.h"
  9. #import "SDImageCachesManagerOperation.h"
  10. #import "SDImageCache.h"
  11. #import "SDInternalMacros.h"
  12. @interface SDImageCachesManager ()
  13. @property (nonatomic, strong, nonnull) dispatch_semaphore_t cachesLock;
  14. @end
  15. @implementation SDImageCachesManager
  16. {
  17. NSMutableArray<id<SDImageCache>> *_imageCaches;
  18. }
  19. + (SDImageCachesManager *)sharedManager {
  20. static dispatch_once_t onceToken;
  21. static SDImageCachesManager *manager;
  22. dispatch_once(&onceToken, ^{
  23. manager = [[SDImageCachesManager alloc] init];
  24. });
  25. return manager;
  26. }
  27. - (instancetype)init {
  28. self = [super init];
  29. if (self) {
  30. self.queryOperationPolicy = SDImageCachesManagerOperationPolicySerial;
  31. self.storeOperationPolicy = SDImageCachesManagerOperationPolicyHighestOnly;
  32. self.removeOperationPolicy = SDImageCachesManagerOperationPolicyConcurrent;
  33. self.containsOperationPolicy = SDImageCachesManagerOperationPolicySerial;
  34. self.clearOperationPolicy = SDImageCachesManagerOperationPolicyConcurrent;
  35. // initialize with default image caches
  36. _imageCaches = [NSMutableArray arrayWithObject:[SDImageCache sharedImageCache]];
  37. _cachesLock = dispatch_semaphore_create(1);
  38. }
  39. return self;
  40. }
  41. - (NSArray<id<SDImageCache>> *)caches {
  42. SD_LOCK(self.cachesLock);
  43. NSArray<id<SDImageCache>> *caches = [_imageCaches copy];
  44. SD_UNLOCK(self.cachesLock);
  45. return caches;
  46. }
  47. - (void)setCaches:(NSArray<id<SDImageCache>> *)caches {
  48. SD_LOCK(self.cachesLock);
  49. [_imageCaches removeAllObjects];
  50. if (caches.count) {
  51. [_imageCaches addObjectsFromArray:caches];
  52. }
  53. SD_UNLOCK(self.cachesLock);
  54. }
  55. #pragma mark - Cache IO operations
  56. - (void)addCache:(id<SDImageCache>)cache {
  57. if (![cache conformsToProtocol:@protocol(SDImageCache)]) {
  58. return;
  59. }
  60. SD_LOCK(self.cachesLock);
  61. [_imageCaches addObject:cache];
  62. SD_UNLOCK(self.cachesLock);
  63. }
  64. - (void)removeCache:(id<SDImageCache>)cache {
  65. if (![cache conformsToProtocol:@protocol(SDImageCache)]) {
  66. return;
  67. }
  68. SD_LOCK(self.cachesLock);
  69. [_imageCaches removeObject:cache];
  70. SD_UNLOCK(self.cachesLock);
  71. }
  72. #pragma mark - SDImageCache
  73. - (id<SDWebImageOperation>)queryImageForKey:(NSString *)key options:(SDWebImageOptions)options context:(SDWebImageContext *)context completion:(SDImageCacheQueryCompletionBlock)completionBlock {
  74. if (!key) {
  75. return nil;
  76. }
  77. NSArray<id<SDImageCache>> *caches = self.caches;
  78. NSUInteger count = caches.count;
  79. if (count == 0) {
  80. return nil;
  81. } else if (count == 1) {
  82. return [caches.firstObject queryImageForKey:key options:options context:context completion:completionBlock];
  83. }
  84. switch (self.queryOperationPolicy) {
  85. case SDImageCachesManagerOperationPolicyHighestOnly: {
  86. id<SDImageCache> cache = caches.lastObject;
  87. return [cache queryImageForKey:key options:options context:context completion:completionBlock];
  88. }
  89. break;
  90. case SDImageCachesManagerOperationPolicyLowestOnly: {
  91. id<SDImageCache> cache = caches.firstObject;
  92. return [cache queryImageForKey:key options:options context:context completion:completionBlock];
  93. }
  94. break;
  95. case SDImageCachesManagerOperationPolicyConcurrent: {
  96. SDImageCachesManagerOperation *operation = [SDImageCachesManagerOperation new];
  97. [operation beginWithTotalCount:caches.count];
  98. [self concurrentQueryImageForKey:key options:options context:context completion:completionBlock enumerator:caches.reverseObjectEnumerator operation:operation];
  99. return operation;
  100. }
  101. break;
  102. case SDImageCachesManagerOperationPolicySerial: {
  103. SDImageCachesManagerOperation *operation = [SDImageCachesManagerOperation new];
  104. [operation beginWithTotalCount:caches.count];
  105. [self serialQueryImageForKey:key options:options context:context completion:completionBlock enumerator:caches.reverseObjectEnumerator operation:operation];
  106. return operation;
  107. }
  108. break;
  109. default:
  110. return nil;
  111. break;
  112. }
  113. }
  114. - (void)storeImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock {
  115. if (!key) {
  116. return;
  117. }
  118. NSArray<id<SDImageCache>> *caches = self.caches;
  119. NSUInteger count = caches.count;
  120. if (count == 0) {
  121. return;
  122. } else if (count == 1) {
  123. [caches.firstObject storeImage:image imageData:imageData forKey:key cacheType:cacheType completion:completionBlock];
  124. return;
  125. }
  126. switch (self.storeOperationPolicy) {
  127. case SDImageCachesManagerOperationPolicyHighestOnly: {
  128. id<SDImageCache> cache = caches.lastObject;
  129. [cache storeImage:image imageData:imageData forKey:key cacheType:cacheType completion:completionBlock];
  130. }
  131. break;
  132. case SDImageCachesManagerOperationPolicyLowestOnly: {
  133. id<SDImageCache> cache = caches.firstObject;
  134. [cache storeImage:image imageData:imageData forKey:key cacheType:cacheType completion:completionBlock];
  135. }
  136. break;
  137. case SDImageCachesManagerOperationPolicyConcurrent: {
  138. SDImageCachesManagerOperation *operation = [SDImageCachesManagerOperation new];
  139. [operation beginWithTotalCount:caches.count];
  140. [self concurrentStoreImage:image imageData:imageData forKey:key cacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator operation:operation];
  141. }
  142. break;
  143. case SDImageCachesManagerOperationPolicySerial: {
  144. [self serialStoreImage:image imageData:imageData forKey:key cacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator];
  145. }
  146. break;
  147. default:
  148. break;
  149. }
  150. }
  151. - (void)removeImageForKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock {
  152. if (!key) {
  153. return;
  154. }
  155. NSArray<id<SDImageCache>> *caches = self.caches;
  156. NSUInteger count = caches.count;
  157. if (count == 0) {
  158. return;
  159. } else if (count == 1) {
  160. [caches.firstObject removeImageForKey:key cacheType:cacheType completion:completionBlock];
  161. return;
  162. }
  163. switch (self.removeOperationPolicy) {
  164. case SDImageCachesManagerOperationPolicyHighestOnly: {
  165. id<SDImageCache> cache = caches.lastObject;
  166. [cache removeImageForKey:key cacheType:cacheType completion:completionBlock];
  167. }
  168. break;
  169. case SDImageCachesManagerOperationPolicyLowestOnly: {
  170. id<SDImageCache> cache = caches.firstObject;
  171. [cache removeImageForKey:key cacheType:cacheType completion:completionBlock];
  172. }
  173. break;
  174. case SDImageCachesManagerOperationPolicyConcurrent: {
  175. SDImageCachesManagerOperation *operation = [SDImageCachesManagerOperation new];
  176. [operation beginWithTotalCount:caches.count];
  177. [self concurrentRemoveImageForKey:key cacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator operation:operation];
  178. }
  179. break;
  180. case SDImageCachesManagerOperationPolicySerial: {
  181. [self serialRemoveImageForKey:key cacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator];
  182. }
  183. break;
  184. default:
  185. break;
  186. }
  187. }
  188. - (void)containsImageForKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(SDImageCacheContainsCompletionBlock)completionBlock {
  189. if (!key) {
  190. return;
  191. }
  192. NSArray<id<SDImageCache>> *caches = self.caches;
  193. NSUInteger count = caches.count;
  194. if (count == 0) {
  195. return;
  196. } else if (count == 1) {
  197. [caches.firstObject containsImageForKey:key cacheType:cacheType completion:completionBlock];
  198. return;
  199. }
  200. switch (self.clearOperationPolicy) {
  201. case SDImageCachesManagerOperationPolicyHighestOnly: {
  202. id<SDImageCache> cache = caches.lastObject;
  203. [cache containsImageForKey:key cacheType:cacheType completion:completionBlock];
  204. }
  205. break;
  206. case SDImageCachesManagerOperationPolicyLowestOnly: {
  207. id<SDImageCache> cache = caches.firstObject;
  208. [cache containsImageForKey:key cacheType:cacheType completion:completionBlock];
  209. }
  210. break;
  211. case SDImageCachesManagerOperationPolicyConcurrent: {
  212. SDImageCachesManagerOperation *operation = [SDImageCachesManagerOperation new];
  213. [operation beginWithTotalCount:caches.count];
  214. [self concurrentContainsImageForKey:key cacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator operation:operation];
  215. }
  216. break;
  217. case SDImageCachesManagerOperationPolicySerial: {
  218. SDImageCachesManagerOperation *operation = [SDImageCachesManagerOperation new];
  219. [operation beginWithTotalCount:caches.count];
  220. [self serialContainsImageForKey:key cacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator operation:operation];
  221. }
  222. break;
  223. default:
  224. break;
  225. }
  226. }
  227. - (void)clearWithCacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock {
  228. NSArray<id<SDImageCache>> *caches = self.caches;
  229. NSUInteger count = caches.count;
  230. if (count == 0) {
  231. return;
  232. } else if (count == 1) {
  233. [caches.firstObject clearWithCacheType:cacheType completion:completionBlock];
  234. return;
  235. }
  236. switch (self.clearOperationPolicy) {
  237. case SDImageCachesManagerOperationPolicyHighestOnly: {
  238. id<SDImageCache> cache = caches.lastObject;
  239. [cache clearWithCacheType:cacheType completion:completionBlock];
  240. }
  241. break;
  242. case SDImageCachesManagerOperationPolicyLowestOnly: {
  243. id<SDImageCache> cache = caches.firstObject;
  244. [cache clearWithCacheType:cacheType completion:completionBlock];
  245. }
  246. break;
  247. case SDImageCachesManagerOperationPolicyConcurrent: {
  248. SDImageCachesManagerOperation *operation = [SDImageCachesManagerOperation new];
  249. [operation beginWithTotalCount:caches.count];
  250. [self concurrentClearWithCacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator operation:operation];
  251. }
  252. break;
  253. case SDImageCachesManagerOperationPolicySerial: {
  254. [self serialClearWithCacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator];
  255. }
  256. break;
  257. default:
  258. break;
  259. }
  260. }
  261. #pragma mark - Concurrent Operation
  262. - (void)concurrentQueryImageForKey:(NSString *)key options:(SDWebImageOptions)options context:(SDWebImageContext *)context completion:(SDImageCacheQueryCompletionBlock)completionBlock enumerator:(NSEnumerator<id<SDImageCache>> *)enumerator operation:(SDImageCachesManagerOperation *)operation {
  263. NSParameterAssert(enumerator);
  264. NSParameterAssert(operation);
  265. for (id<SDImageCache> cache in enumerator) {
  266. [cache queryImageForKey:key options:options context:context completion:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) {
  267. if (operation.isCancelled) {
  268. // Cancelled
  269. return;
  270. }
  271. if (operation.isFinished) {
  272. // Finished
  273. return;
  274. }
  275. [operation completeOne];
  276. if (image) {
  277. // Success
  278. [operation done];
  279. if (completionBlock) {
  280. completionBlock(image, data, cacheType);
  281. }
  282. return;
  283. }
  284. if (operation.pendingCount == 0) {
  285. // Complete
  286. [operation done];
  287. if (completionBlock) {
  288. completionBlock(nil, nil, SDImageCacheTypeNone);
  289. }
  290. }
  291. }];
  292. }
  293. }
  294. - (void)concurrentStoreImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock enumerator:(NSEnumerator<id<SDImageCache>> *)enumerator operation:(SDImageCachesManagerOperation *)operation {
  295. NSParameterAssert(enumerator);
  296. NSParameterAssert(operation);
  297. for (id<SDImageCache> cache in enumerator) {
  298. [cache storeImage:image imageData:imageData forKey:key cacheType:cacheType completion:^{
  299. if (operation.isCancelled) {
  300. // Cancelled
  301. return;
  302. }
  303. if (operation.isFinished) {
  304. // Finished
  305. return;
  306. }
  307. [operation completeOne];
  308. if (operation.pendingCount == 0) {
  309. // Complete
  310. [operation done];
  311. if (completionBlock) {
  312. completionBlock();
  313. }
  314. }
  315. }];
  316. }
  317. }
  318. - (void)concurrentRemoveImageForKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock enumerator:(NSEnumerator<id<SDImageCache>> *)enumerator operation:(SDImageCachesManagerOperation *)operation {
  319. NSParameterAssert(enumerator);
  320. NSParameterAssert(operation);
  321. for (id<SDImageCache> cache in enumerator) {
  322. [cache removeImageForKey:key cacheType:cacheType completion:^{
  323. if (operation.isCancelled) {
  324. // Cancelled
  325. return;
  326. }
  327. if (operation.isFinished) {
  328. // Finished
  329. return;
  330. }
  331. [operation completeOne];
  332. if (operation.pendingCount == 0) {
  333. // Complete
  334. [operation done];
  335. if (completionBlock) {
  336. completionBlock();
  337. }
  338. }
  339. }];
  340. }
  341. }
  342. - (void)concurrentContainsImageForKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(SDImageCacheContainsCompletionBlock)completionBlock enumerator:(NSEnumerator<id<SDImageCache>> *)enumerator operation:(SDImageCachesManagerOperation *)operation {
  343. NSParameterAssert(enumerator);
  344. NSParameterAssert(operation);
  345. for (id<SDImageCache> cache in enumerator) {
  346. [cache containsImageForKey:key cacheType:cacheType completion:^(SDImageCacheType containsCacheType) {
  347. if (operation.isCancelled) {
  348. // Cancelled
  349. return;
  350. }
  351. if (operation.isFinished) {
  352. // Finished
  353. return;
  354. }
  355. [operation completeOne];
  356. if (containsCacheType != SDImageCacheTypeNone) {
  357. // Success
  358. [operation done];
  359. if (completionBlock) {
  360. completionBlock(containsCacheType);
  361. }
  362. return;
  363. }
  364. if (operation.pendingCount == 0) {
  365. // Complete
  366. [operation done];
  367. if (completionBlock) {
  368. completionBlock(SDImageCacheTypeNone);
  369. }
  370. }
  371. }];
  372. }
  373. }
  374. - (void)concurrentClearWithCacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock enumerator:(NSEnumerator<id<SDImageCache>> *)enumerator operation:(SDImageCachesManagerOperation *)operation {
  375. NSParameterAssert(enumerator);
  376. NSParameterAssert(operation);
  377. for (id<SDImageCache> cache in enumerator) {
  378. [cache clearWithCacheType:cacheType completion:^{
  379. if (operation.isCancelled) {
  380. // Cancelled
  381. return;
  382. }
  383. if (operation.isFinished) {
  384. // Finished
  385. return;
  386. }
  387. [operation completeOne];
  388. if (operation.pendingCount == 0) {
  389. // Complete
  390. [operation done];
  391. if (completionBlock) {
  392. completionBlock();
  393. }
  394. }
  395. }];
  396. }
  397. }
  398. #pragma mark - Serial Operation
  399. - (void)serialQueryImageForKey:(NSString *)key options:(SDWebImageOptions)options context:(SDWebImageContext *)context completion:(SDImageCacheQueryCompletionBlock)completionBlock enumerator:(NSEnumerator<id<SDImageCache>> *)enumerator operation:(SDImageCachesManagerOperation *)operation {
  400. NSParameterAssert(enumerator);
  401. NSParameterAssert(operation);
  402. id<SDImageCache> cache = enumerator.nextObject;
  403. if (!cache) {
  404. // Complete
  405. [operation done];
  406. if (completionBlock) {
  407. completionBlock(nil, nil, SDImageCacheTypeNone);
  408. }
  409. return;
  410. }
  411. @weakify(self);
  412. [cache queryImageForKey:key options:options context:context completion:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) {
  413. @strongify(self);
  414. if (operation.isCancelled) {
  415. // Cancelled
  416. return;
  417. }
  418. if (operation.isFinished) {
  419. // Finished
  420. return;
  421. }
  422. [operation completeOne];
  423. if (image) {
  424. // Success
  425. [operation done];
  426. if (completionBlock) {
  427. completionBlock(image, data, cacheType);
  428. }
  429. return;
  430. }
  431. // Next
  432. [self serialQueryImageForKey:key options:options context:context completion:completionBlock enumerator:enumerator operation:operation];
  433. }];
  434. }
  435. - (void)serialStoreImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock enumerator:(NSEnumerator<id<SDImageCache>> *)enumerator {
  436. NSParameterAssert(enumerator);
  437. id<SDImageCache> cache = enumerator.nextObject;
  438. if (!cache) {
  439. // Complete
  440. if (completionBlock) {
  441. completionBlock();
  442. }
  443. return;
  444. }
  445. @weakify(self);
  446. [cache storeImage:image imageData:imageData forKey:key cacheType:cacheType completion:^{
  447. @strongify(self);
  448. // Next
  449. [self serialStoreImage:image imageData:imageData forKey:key cacheType:cacheType completion:completionBlock enumerator:enumerator];
  450. }];
  451. }
  452. - (void)serialRemoveImageForKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock enumerator:(NSEnumerator<id<SDImageCache>> *)enumerator {
  453. NSParameterAssert(enumerator);
  454. id<SDImageCache> cache = enumerator.nextObject;
  455. if (!cache) {
  456. // Complete
  457. if (completionBlock) {
  458. completionBlock();
  459. }
  460. return;
  461. }
  462. @weakify(self);
  463. [cache removeImageForKey:key cacheType:cacheType completion:^{
  464. @strongify(self);
  465. // Next
  466. [self serialRemoveImageForKey:key cacheType:cacheType completion:completionBlock enumerator:enumerator];
  467. }];
  468. }
  469. - (void)serialContainsImageForKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(SDImageCacheContainsCompletionBlock)completionBlock enumerator:(NSEnumerator<id<SDImageCache>> *)enumerator operation:(SDImageCachesManagerOperation *)operation {
  470. NSParameterAssert(enumerator);
  471. NSParameterAssert(operation);
  472. id<SDImageCache> cache = enumerator.nextObject;
  473. if (!cache) {
  474. // Complete
  475. [operation done];
  476. if (completionBlock) {
  477. completionBlock(SDImageCacheTypeNone);
  478. }
  479. return;
  480. }
  481. @weakify(self);
  482. [cache containsImageForKey:key cacheType:cacheType completion:^(SDImageCacheType containsCacheType) {
  483. @strongify(self);
  484. if (operation.isCancelled) {
  485. // Cancelled
  486. return;
  487. }
  488. if (operation.isFinished) {
  489. // Finished
  490. return;
  491. }
  492. [operation completeOne];
  493. if (containsCacheType != SDImageCacheTypeNone) {
  494. // Success
  495. [operation done];
  496. if (completionBlock) {
  497. completionBlock(containsCacheType);
  498. }
  499. return;
  500. }
  501. // Next
  502. [self serialContainsImageForKey:key cacheType:cacheType completion:completionBlock enumerator:enumerator operation:operation];
  503. }];
  504. }
  505. - (void)serialClearWithCacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock enumerator:(NSEnumerator<id<SDImageCache>> *)enumerator {
  506. NSParameterAssert(enumerator);
  507. id<SDImageCache> cache = enumerator.nextObject;
  508. if (!cache) {
  509. // Complete
  510. if (completionBlock) {
  511. completionBlock();
  512. }
  513. return;
  514. }
  515. @weakify(self);
  516. [cache clearWithCacheType:cacheType completion:^{
  517. @strongify(self);
  518. // Next
  519. [self serialClearWithCacheType:cacheType completion:completionBlock enumerator:enumerator];
  520. }];
  521. }
  522. @end