TMDiskCache.m 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063
  1. #import "TMDiskCache.h"
  2. #import "TMCacheBackgroundTaskManager.h"
  3. #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_0
  4. #import <UIKit/UIKit.h>
  5. #endif
  6. #define TMDiskCacheError(error) if (error) { NSLog(@"%@ (%d) ERROR: %@", \
  7. [[NSString stringWithUTF8String:__FILE__] lastPathComponent], \
  8. __LINE__, [error localizedDescription]); }
  9. static id <TMCacheBackgroundTaskManager> TMCacheBackgroundTaskManager;
  10. NSString * const TMDiskCachePrefix = @"com.tumblr.TMDiskCache";
  11. NSString * const TMDiskCacheSharedName = @"TMDiskCacheShared";
  12. @interface TMDiskCache ()
  13. @property (assign) NSUInteger byteCount;
  14. @property (strong, nonatomic) NSURL *cacheURL;
  15. @property (assign, nonatomic) dispatch_queue_t queue;
  16. @property (strong, nonatomic) NSMutableDictionary *dates;
  17. @property (strong, nonatomic) NSMutableDictionary *sizes;
  18. @end
  19. @implementation TMDiskCache
  20. @synthesize willAddObjectBlock = _willAddObjectBlock;
  21. @synthesize willRemoveObjectBlock = _willRemoveObjectBlock;
  22. @synthesize willRemoveAllObjectsBlock = _willRemoveAllObjectsBlock;
  23. @synthesize didAddObjectBlock = _didAddObjectBlock;
  24. @synthesize didRemoveObjectBlock = _didRemoveObjectBlock;
  25. @synthesize didRemoveAllObjectsBlock = _didRemoveAllObjectsBlock;
  26. @synthesize byteLimit = _byteLimit;
  27. @synthesize ageLimit = _ageLimit;
  28. #pragma mark - Initialization -
  29. - (instancetype)initWithName:(NSString *)name
  30. {
  31. return [self initWithName:name rootPath:[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0]];
  32. }
  33. - (instancetype)initWithName:(NSString *)name rootPath:(NSString *)rootPath
  34. {
  35. if (!name)
  36. return nil;
  37. if (self = [super init]) {
  38. _name = [name copy];
  39. _queue = [TMDiskCache sharedQueue];
  40. _willAddObjectBlock = nil;
  41. _willRemoveObjectBlock = nil;
  42. _willRemoveAllObjectsBlock = nil;
  43. _didAddObjectBlock = nil;
  44. _didRemoveObjectBlock = nil;
  45. _didRemoveAllObjectsBlock = nil;
  46. _byteCount = 0;
  47. _byteLimit = 0;
  48. _ageLimit = 0.0;
  49. _dates = [[NSMutableDictionary alloc] init];
  50. _sizes = [[NSMutableDictionary alloc] init];
  51. NSString *pathComponent = [[NSString alloc] initWithFormat:@"%@.%@", TMDiskCachePrefix, _name];
  52. _cacheURL = [NSURL fileURLWithPathComponents:@[ rootPath, pathComponent ]];
  53. __weak TMDiskCache *weakSelf = self;
  54. dispatch_async(_queue, ^{
  55. TMDiskCache *strongSelf = weakSelf;
  56. [strongSelf createCacheDirectory];
  57. [strongSelf initializeDiskProperties];
  58. });
  59. }
  60. return self;
  61. }
  62. - (NSString *)description
  63. {
  64. return [[NSString alloc] initWithFormat:@"%@.%@.%p", TMDiskCachePrefix, _name, self];
  65. }
  66. + (instancetype)sharedCache
  67. {
  68. static id cache;
  69. static dispatch_once_t predicate;
  70. dispatch_once(&predicate, ^{
  71. cache = [[self alloc] initWithName:TMDiskCacheSharedName];
  72. });
  73. return cache;
  74. }
  75. + (dispatch_queue_t)sharedQueue
  76. {
  77. static dispatch_queue_t queue;
  78. static dispatch_once_t predicate;
  79. dispatch_once(&predicate, ^{
  80. queue = dispatch_queue_create([TMDiskCachePrefix UTF8String], DISPATCH_QUEUE_SERIAL);
  81. });
  82. return queue;
  83. }
  84. #pragma mark - Private Methods -
  85. - (NSURL *)encodedFileURLForKey:(NSString *)key
  86. {
  87. if (![key length])
  88. return nil;
  89. return [_cacheURL URLByAppendingPathComponent:[self encodedString:key]];
  90. }
  91. - (NSString *)keyForEncodedFileURL:(NSURL *)url
  92. {
  93. NSString *fileName = [url lastPathComponent];
  94. if (!fileName)
  95. return nil;
  96. return [self decodedString:fileName];
  97. }
  98. - (NSString *)encodedString:(NSString *)string
  99. {
  100. if (![string length])
  101. return @"";
  102. CFStringRef static const charsToEscape = CFSTR(".:/");
  103. CFStringRef escapedString = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
  104. (__bridge CFStringRef)string,
  105. NULL,
  106. charsToEscape,
  107. kCFStringEncodingUTF8);
  108. return (__bridge_transfer NSString *)escapedString;
  109. }
  110. - (NSString *)decodedString:(NSString *)string
  111. {
  112. if (![string length])
  113. return @"";
  114. CFStringRef unescapedString = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault,
  115. (__bridge CFStringRef)string,
  116. CFSTR(""),
  117. kCFStringEncodingUTF8);
  118. return (__bridge_transfer NSString *)unescapedString;
  119. }
  120. #pragma mark - Private Trash Methods -
  121. + (dispatch_queue_t)sharedTrashQueue
  122. {
  123. static dispatch_queue_t trashQueue;
  124. static dispatch_once_t predicate;
  125. dispatch_once(&predicate, ^{
  126. NSString *queueName = [[NSString alloc] initWithFormat:@"%@.trash", TMDiskCachePrefix];
  127. trashQueue = dispatch_queue_create([queueName UTF8String], DISPATCH_QUEUE_SERIAL);
  128. dispatch_set_target_queue(trashQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
  129. });
  130. return trashQueue;
  131. }
  132. + (NSURL *)sharedTrashURL
  133. {
  134. static NSURL *sharedTrashURL;
  135. static dispatch_once_t predicate;
  136. dispatch_once(&predicate, ^{
  137. sharedTrashURL = [[[NSURL alloc] initFileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:TMDiskCachePrefix isDirectory:YES];
  138. if (![[NSFileManager defaultManager] fileExistsAtPath:[sharedTrashURL path]]) {
  139. NSError *error = nil;
  140. [[NSFileManager defaultManager] createDirectoryAtURL:sharedTrashURL
  141. withIntermediateDirectories:YES
  142. attributes:nil
  143. error:&error];
  144. TMDiskCacheError(error);
  145. }
  146. });
  147. return sharedTrashURL;
  148. }
  149. +(BOOL)moveItemAtURLToTrash:(NSURL *)itemURL
  150. {
  151. if (![[NSFileManager defaultManager] fileExistsAtPath:[itemURL path]])
  152. return NO;
  153. NSError *error = nil;
  154. NSString *uniqueString = [[NSProcessInfo processInfo] globallyUniqueString];
  155. NSURL *uniqueTrashURL = [[TMDiskCache sharedTrashURL] URLByAppendingPathComponent:uniqueString];
  156. BOOL moved = [[NSFileManager defaultManager] moveItemAtURL:itemURL toURL:uniqueTrashURL error:&error];
  157. TMDiskCacheError(error);
  158. return moved;
  159. }
  160. + (void)emptyTrash
  161. {
  162. UIBackgroundTaskIdentifier taskID = [TMCacheBackgroundTaskManager beginBackgroundTask];
  163. dispatch_async([self sharedTrashQueue], ^{
  164. NSError *error = nil;
  165. NSArray *trashedItems = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:[self sharedTrashURL]
  166. includingPropertiesForKeys:nil
  167. options:0
  168. error:&error];
  169. TMDiskCacheError(error);
  170. for (NSURL *trashedItemURL in trashedItems) {
  171. NSError *error = nil;
  172. [[NSFileManager defaultManager] removeItemAtURL:trashedItemURL error:&error];
  173. TMDiskCacheError(error);
  174. }
  175. [TMCacheBackgroundTaskManager endBackgroundTask:taskID];
  176. });
  177. }
  178. #pragma mark - Private Queue Methods -
  179. - (BOOL)createCacheDirectory
  180. {
  181. if ([[NSFileManager defaultManager] fileExistsAtPath:[_cacheURL path]])
  182. return NO;
  183. NSError *error = nil;
  184. BOOL success = [[NSFileManager defaultManager] createDirectoryAtURL:_cacheURL
  185. withIntermediateDirectories:YES
  186. attributes:nil
  187. error:&error];
  188. TMDiskCacheError(error);
  189. return success;
  190. }
  191. - (void)initializeDiskProperties
  192. {
  193. NSUInteger byteCount = 0;
  194. NSArray *keys = @[ NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey ];
  195. NSError *error = nil;
  196. NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:_cacheURL
  197. includingPropertiesForKeys:keys
  198. options:NSDirectoryEnumerationSkipsHiddenFiles
  199. error:&error];
  200. TMDiskCacheError(error);
  201. for (NSURL *fileURL in files) {
  202. NSString *key = [self keyForEncodedFileURL:fileURL];
  203. error = nil;
  204. NSDictionary *dictionary = [fileURL resourceValuesForKeys:keys error:&error];
  205. TMDiskCacheError(error);
  206. NSDate *date = [dictionary objectForKey:NSURLContentModificationDateKey];
  207. if (date && key)
  208. [_dates setObject:date forKey:key];
  209. NSNumber *fileSize = [dictionary objectForKey:NSURLTotalFileAllocatedSizeKey];
  210. if (fileSize) {
  211. [_sizes setObject:fileSize forKey:key];
  212. byteCount += [fileSize unsignedIntegerValue];
  213. }
  214. }
  215. if (byteCount > 0)
  216. self.byteCount = byteCount; // atomic
  217. }
  218. - (BOOL)setFileModificationDate:(NSDate *)date forURL:(NSURL *)fileURL
  219. {
  220. if (!date || !fileURL) {
  221. return NO;
  222. }
  223. NSError *error = nil;
  224. BOOL success = [[NSFileManager defaultManager] setAttributes:@{ NSFileModificationDate: date }
  225. ofItemAtPath:[fileURL path]
  226. error:&error];
  227. TMDiskCacheError(error);
  228. if (success) {
  229. NSString *key = [self keyForEncodedFileURL:fileURL];
  230. if (key) {
  231. [_dates setObject:date forKey:key];
  232. }
  233. }
  234. return success;
  235. }
  236. - (BOOL)removeFileAndExecuteBlocksForKey:(NSString *)key
  237. {
  238. NSURL *fileURL = [self encodedFileURLForKey:key];
  239. if (!fileURL || ![[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]])
  240. return NO;
  241. if (_willRemoveObjectBlock)
  242. _willRemoveObjectBlock(self, key, nil, fileURL);
  243. BOOL trashed = [TMDiskCache moveItemAtURLToTrash:fileURL];
  244. if (!trashed)
  245. return NO;
  246. [TMDiskCache emptyTrash];
  247. NSNumber *byteSize = [_sizes objectForKey:key];
  248. if (byteSize)
  249. self.byteCount = _byteCount - [byteSize unsignedIntegerValue]; // atomic
  250. [_sizes removeObjectForKey:key];
  251. [_dates removeObjectForKey:key];
  252. if (_didRemoveObjectBlock)
  253. _didRemoveObjectBlock(self, key, nil, fileURL);
  254. return YES;
  255. }
  256. - (void)trimDiskToSize:(NSUInteger)trimByteCount
  257. {
  258. if (_byteCount <= trimByteCount)
  259. return;
  260. NSArray *keysSortedBySize = [_sizes keysSortedByValueUsingSelector:@selector(compare:)];
  261. for (NSString *key in [keysSortedBySize reverseObjectEnumerator]) { // largest objects first
  262. [self removeFileAndExecuteBlocksForKey:key];
  263. if (_byteCount <= trimByteCount)
  264. break;
  265. }
  266. }
  267. - (void)trimDiskToSizeByDate:(NSUInteger)trimByteCount
  268. {
  269. if (_byteCount <= trimByteCount)
  270. return;
  271. NSArray *keysSortedByDate = [_dates keysSortedByValueUsingSelector:@selector(compare:)];
  272. for (NSString *key in keysSortedByDate) { // oldest objects first
  273. [self removeFileAndExecuteBlocksForKey:key];
  274. if (_byteCount <= trimByteCount)
  275. break;
  276. }
  277. }
  278. - (void)trimDiskToDate:(NSDate *)trimDate
  279. {
  280. NSArray *keysSortedByDate = [_dates keysSortedByValueUsingSelector:@selector(compare:)];
  281. for (NSString *key in keysSortedByDate) { // oldest files first
  282. NSDate *accessDate = [_dates objectForKey:key];
  283. if (!accessDate)
  284. continue;
  285. if ([accessDate compare:trimDate] == NSOrderedAscending) { // older than trim date
  286. [self removeFileAndExecuteBlocksForKey:key];
  287. } else {
  288. break;
  289. }
  290. }
  291. }
  292. - (void)trimToAgeLimitRecursively
  293. {
  294. if (_ageLimit == 0.0)
  295. return;
  296. NSDate *date = [[NSDate alloc] initWithTimeIntervalSinceNow:-_ageLimit];
  297. [self trimDiskToDate:date];
  298. __weak TMDiskCache *weakSelf = self;
  299. dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_ageLimit * NSEC_PER_SEC));
  300. dispatch_after(time, _queue, ^(void) {
  301. TMDiskCache *strongSelf = weakSelf;
  302. [strongSelf trimToAgeLimitRecursively];
  303. });
  304. }
  305. #pragma mark - Public Asynchronous Methods -
  306. - (void)objectForKey:(NSString *)key block:(TMDiskCacheObjectBlock)block
  307. {
  308. NSDate *now = [[NSDate alloc] init];
  309. if (!key || !block)
  310. return;
  311. __weak TMDiskCache *weakSelf = self;
  312. dispatch_async(_queue, ^{
  313. TMDiskCache *strongSelf = weakSelf;
  314. if (!strongSelf)
  315. return;
  316. NSURL *fileURL = [strongSelf encodedFileURLForKey:key];
  317. id <NSCoding> object = nil;
  318. if ([[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]]) {
  319. @try {
  320. object = [NSKeyedUnarchiver unarchiveObjectWithFile:[fileURL path]];
  321. }
  322. @catch (NSException *exception) {
  323. NSError *error = nil;
  324. [[NSFileManager defaultManager] removeItemAtPath:[fileURL path] error:&error];
  325. TMDiskCacheError(error);
  326. }
  327. [strongSelf setFileModificationDate:now forURL:fileURL];
  328. }
  329. block(strongSelf, key, object, fileURL);
  330. });
  331. }
  332. - (void)fileURLForKey:(NSString *)key block:(TMDiskCacheObjectBlock)block
  333. {
  334. NSDate *now = [[NSDate alloc] init];
  335. if (!key || !block)
  336. return;
  337. __weak TMDiskCache *weakSelf = self;
  338. dispatch_async(_queue, ^{
  339. TMDiskCache *strongSelf = weakSelf;
  340. if (!strongSelf)
  341. return;
  342. NSURL *fileURL = [strongSelf encodedFileURLForKey:key];
  343. if ([[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]]) {
  344. [strongSelf setFileModificationDate:now forURL:fileURL];
  345. } else {
  346. fileURL = nil;
  347. }
  348. block(strongSelf, key, nil, fileURL);
  349. });
  350. }
  351. - (void)setObject:(id <NSCoding>)object forKey:(NSString *)key block:(TMDiskCacheObjectBlock)block
  352. {
  353. NSDate *now = [[NSDate alloc] init];
  354. if (!key || !object)
  355. return;
  356. UIBackgroundTaskIdentifier taskID = [TMCacheBackgroundTaskManager beginBackgroundTask];
  357. __weak TMDiskCache *weakSelf = self;
  358. dispatch_async(_queue, ^{
  359. TMDiskCache *strongSelf = weakSelf;
  360. if (!strongSelf) {
  361. [TMCacheBackgroundTaskManager endBackgroundTask:taskID];
  362. return;
  363. }
  364. NSURL *fileURL = [strongSelf encodedFileURLForKey:key];
  365. if (strongSelf->_willAddObjectBlock)
  366. strongSelf->_willAddObjectBlock(strongSelf, key, object, fileURL);
  367. BOOL written = [NSKeyedArchiver archiveRootObject:object toFile:[fileURL path]];
  368. if (written) {
  369. [strongSelf setFileModificationDate:now forURL:fileURL];
  370. NSError *error = nil;
  371. NSDictionary *values = [fileURL resourceValuesForKeys:@[ NSURLTotalFileAllocatedSizeKey ] error:&error];
  372. TMDiskCacheError(error);
  373. NSNumber *diskFileSize = [values objectForKey:NSURLTotalFileAllocatedSizeKey];
  374. if (diskFileSize) {
  375. NSNumber *oldEntry = [strongSelf->_sizes objectForKey:key];
  376. if ([oldEntry isKindOfClass:[NSNumber class]]){
  377. strongSelf.byteCount = strongSelf->_byteCount - [oldEntry unsignedIntegerValue];
  378. }
  379. [strongSelf->_sizes setObject:diskFileSize forKey:key];
  380. strongSelf.byteCount = strongSelf->_byteCount + [diskFileSize unsignedIntegerValue]; // atomic
  381. }
  382. if (strongSelf->_byteLimit > 0 && strongSelf->_byteCount > strongSelf->_byteLimit)
  383. [strongSelf trimToSizeByDate:strongSelf->_byteLimit block:nil];
  384. } else {
  385. fileURL = nil;
  386. }
  387. if (strongSelf->_didAddObjectBlock)
  388. strongSelf->_didAddObjectBlock(strongSelf, key, object, written ? fileURL : nil);
  389. if (block)
  390. block(strongSelf, key, object, fileURL);
  391. [TMCacheBackgroundTaskManager endBackgroundTask:taskID];
  392. });
  393. }
  394. - (void)removeObjectForKey:(NSString *)key block:(TMDiskCacheObjectBlock)block
  395. {
  396. if (!key)
  397. return;
  398. UIBackgroundTaskIdentifier taskID = [TMCacheBackgroundTaskManager beginBackgroundTask];
  399. __weak TMDiskCache *weakSelf = self;
  400. dispatch_async(_queue, ^{
  401. TMDiskCache *strongSelf = weakSelf;
  402. if (!strongSelf) {
  403. [TMCacheBackgroundTaskManager endBackgroundTask:taskID];
  404. return;
  405. }
  406. NSURL *fileURL = [strongSelf encodedFileURLForKey:key];
  407. [strongSelf removeFileAndExecuteBlocksForKey:key];
  408. if (block)
  409. block(strongSelf, key, nil, fileURL);
  410. [TMCacheBackgroundTaskManager endBackgroundTask:taskID];
  411. });
  412. }
  413. - (void)trimToSize:(NSUInteger)trimByteCount block:(TMDiskCacheBlock)block
  414. {
  415. if (trimByteCount == 0) {
  416. [self removeAllObjects:block];
  417. return;
  418. }
  419. UIBackgroundTaskIdentifier taskID = [TMCacheBackgroundTaskManager beginBackgroundTask];
  420. __weak TMDiskCache *weakSelf = self;
  421. dispatch_async(_queue, ^{
  422. TMDiskCache *strongSelf = weakSelf;
  423. if (!strongSelf) {
  424. [TMCacheBackgroundTaskManager endBackgroundTask:taskID];
  425. return;
  426. }
  427. [strongSelf trimDiskToSize:trimByteCount];
  428. if (block)
  429. block(strongSelf);
  430. [TMCacheBackgroundTaskManager endBackgroundTask:taskID];
  431. });
  432. }
  433. - (void)trimToDate:(NSDate *)trimDate block:(TMDiskCacheBlock)block
  434. {
  435. if (!trimDate)
  436. return;
  437. if ([trimDate isEqualToDate:[NSDate distantPast]]) {
  438. [self removeAllObjects:block];
  439. return;
  440. }
  441. UIBackgroundTaskIdentifier taskID = [TMCacheBackgroundTaskManager beginBackgroundTask];
  442. __weak TMDiskCache *weakSelf = self;
  443. dispatch_async(_queue, ^{
  444. TMDiskCache *strongSelf = weakSelf;
  445. if (!strongSelf) {
  446. [TMCacheBackgroundTaskManager endBackgroundTask:taskID];
  447. return;
  448. }
  449. [strongSelf trimDiskToDate:trimDate];
  450. if (block)
  451. block(strongSelf);
  452. [TMCacheBackgroundTaskManager endBackgroundTask:taskID];
  453. });
  454. }
  455. - (void)trimToSizeByDate:(NSUInteger)trimByteCount block:(TMDiskCacheBlock)block
  456. {
  457. if (trimByteCount == 0) {
  458. [self removeAllObjects:block];
  459. return;
  460. }
  461. UIBackgroundTaskIdentifier taskID = [TMCacheBackgroundTaskManager beginBackgroundTask];
  462. __weak TMDiskCache *weakSelf = self;
  463. dispatch_async(_queue, ^{
  464. TMDiskCache *strongSelf = weakSelf;
  465. if (!strongSelf) {
  466. [TMCacheBackgroundTaskManager endBackgroundTask:taskID];
  467. return;
  468. }
  469. [strongSelf trimDiskToSizeByDate:trimByteCount];
  470. if (block)
  471. block(strongSelf);
  472. [TMCacheBackgroundTaskManager endBackgroundTask:taskID];
  473. });
  474. }
  475. - (void)removeAllObjects:(TMDiskCacheBlock)block
  476. {
  477. UIBackgroundTaskIdentifier taskID = [TMCacheBackgroundTaskManager beginBackgroundTask];
  478. __weak TMDiskCache *weakSelf = self;
  479. dispatch_async(_queue, ^{
  480. TMDiskCache *strongSelf = weakSelf;
  481. if (!strongSelf) {
  482. [TMCacheBackgroundTaskManager endBackgroundTask:taskID];
  483. return;
  484. }
  485. if (strongSelf->_willRemoveAllObjectsBlock)
  486. strongSelf->_willRemoveAllObjectsBlock(strongSelf);
  487. [TMDiskCache moveItemAtURLToTrash:strongSelf->_cacheURL];
  488. [TMDiskCache emptyTrash];
  489. [strongSelf createCacheDirectory];
  490. [strongSelf->_dates removeAllObjects];
  491. [strongSelf->_sizes removeAllObjects];
  492. strongSelf.byteCount = 0; // atomic
  493. if (strongSelf->_didRemoveAllObjectsBlock)
  494. strongSelf->_didRemoveAllObjectsBlock(strongSelf);
  495. if (block)
  496. block(strongSelf);
  497. [TMCacheBackgroundTaskManager endBackgroundTask:taskID];
  498. });
  499. }
  500. - (void)enumerateObjectsWithBlock:(TMDiskCacheObjectBlock)block completionBlock:(TMDiskCacheBlock)completionBlock
  501. {
  502. if (!block)
  503. return;
  504. UIBackgroundTaskIdentifier taskID = [TMCacheBackgroundTaskManager beginBackgroundTask];
  505. __weak TMDiskCache *weakSelf = self;
  506. dispatch_async(_queue, ^{
  507. TMDiskCache *strongSelf = weakSelf;
  508. if (!strongSelf) {
  509. [TMCacheBackgroundTaskManager endBackgroundTask:taskID];
  510. return;
  511. }
  512. NSArray *keysSortedByDate = [strongSelf->_dates keysSortedByValueUsingSelector:@selector(compare:)];
  513. for (NSString *key in keysSortedByDate) {
  514. NSURL *fileURL = [strongSelf encodedFileURLForKey:key];
  515. block(strongSelf, key, nil, fileURL);
  516. }
  517. if (completionBlock)
  518. completionBlock(strongSelf);
  519. [TMCacheBackgroundTaskManager endBackgroundTask:taskID];
  520. });
  521. }
  522. #pragma mark - Public Synchronous Methods -
  523. - (id <NSCoding>)objectForKey:(NSString *)key
  524. {
  525. if (!key)
  526. return nil;
  527. __block id <NSCoding> objectForKey = nil;
  528. dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  529. [self objectForKey:key block:^(TMDiskCache *cache, NSString *key, id <NSCoding> object, NSURL *fileURL) {
  530. objectForKey = object;
  531. dispatch_semaphore_signal(semaphore);
  532. }];
  533. dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  534. #if !OS_OBJECT_USE_OBJC
  535. dispatch_release(semaphore);
  536. #endif
  537. return objectForKey;
  538. }
  539. - (NSURL *)fileURLForKey:(NSString *)key
  540. {
  541. if (!key)
  542. return nil;
  543. __block NSURL *fileURLForKey = nil;
  544. dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  545. [self fileURLForKey:key block:^(TMDiskCache *cache, NSString *key, id <NSCoding> object, NSURL *fileURL) {
  546. fileURLForKey = fileURL;
  547. dispatch_semaphore_signal(semaphore);
  548. }];
  549. dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  550. #if !OS_OBJECT_USE_OBJC
  551. dispatch_release(semaphore);
  552. #endif
  553. return fileURLForKey;
  554. }
  555. - (void)setObject:(id <NSCoding>)object forKey:(NSString *)key
  556. {
  557. if (!object || !key)
  558. return;
  559. dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  560. [self setObject:object forKey:key block:^(TMDiskCache *cache, NSString *key, id <NSCoding> object, NSURL *fileURL) {
  561. dispatch_semaphore_signal(semaphore);
  562. }];
  563. dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  564. #if !OS_OBJECT_USE_OBJC
  565. dispatch_release(semaphore);
  566. #endif
  567. }
  568. - (void)removeObjectForKey:(NSString *)key
  569. {
  570. if (!key)
  571. return;
  572. dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  573. [self removeObjectForKey:key block:^(TMDiskCache *cache, NSString *key, id <NSCoding> object, NSURL *fileURL) {
  574. dispatch_semaphore_signal(semaphore);
  575. }];
  576. dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  577. #if !OS_OBJECT_USE_OBJC
  578. dispatch_release(semaphore);
  579. #endif
  580. }
  581. - (void)trimToSize:(NSUInteger)byteCount
  582. {
  583. dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  584. [self trimToSize:byteCount block:^(TMDiskCache *cache) {
  585. dispatch_semaphore_signal(semaphore);
  586. }];
  587. dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  588. #if !OS_OBJECT_USE_OBJC
  589. dispatch_release(semaphore);
  590. #endif
  591. }
  592. - (void)trimToDate:(NSDate *)date
  593. {
  594. if (!date)
  595. return;
  596. if ([date isEqualToDate:[NSDate distantPast]]) {
  597. [self removeAllObjects];
  598. return;
  599. }
  600. dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  601. [self trimToDate:date block:^(TMDiskCache *cache) {
  602. dispatch_semaphore_signal(semaphore);
  603. }];
  604. dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  605. #if !OS_OBJECT_USE_OBJC
  606. dispatch_release(semaphore);
  607. #endif
  608. }
  609. - (void)trimToSizeByDate:(NSUInteger)byteCount
  610. {
  611. dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  612. [self trimToSizeByDate:byteCount block:^(TMDiskCache *cache) {
  613. dispatch_semaphore_signal(semaphore);
  614. }];
  615. dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  616. #if !OS_OBJECT_USE_OBJC
  617. dispatch_release(semaphore);
  618. #endif
  619. }
  620. - (void)removeAllObjects
  621. {
  622. dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  623. [self removeAllObjects:^(TMDiskCache *cache) {
  624. dispatch_semaphore_signal(semaphore);
  625. }];
  626. dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  627. #if !OS_OBJECT_USE_OBJC
  628. dispatch_release(semaphore);
  629. #endif
  630. }
  631. - (void)enumerateObjectsWithBlock:(TMDiskCacheObjectBlock)block
  632. {
  633. if (!block)
  634. return;
  635. dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  636. [self enumerateObjectsWithBlock:block completionBlock:^(TMDiskCache *cache) {
  637. dispatch_semaphore_signal(semaphore);
  638. }];
  639. dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  640. #if !OS_OBJECT_USE_OBJC
  641. dispatch_release(semaphore);
  642. #endif
  643. }
  644. #pragma mark - Public Thread Safe Accessors -
  645. - (TMDiskCacheObjectBlock)willAddObjectBlock
  646. {
  647. __block TMDiskCacheObjectBlock block = nil;
  648. dispatch_sync(_queue, ^{
  649. block = _willAddObjectBlock;
  650. });
  651. return block;
  652. }
  653. - (void)setWillAddObjectBlock:(TMDiskCacheObjectBlock)block
  654. {
  655. __weak TMDiskCache *weakSelf = self;
  656. dispatch_async(_queue, ^{
  657. TMDiskCache *strongSelf = weakSelf;
  658. if (!strongSelf)
  659. return;
  660. strongSelf->_willAddObjectBlock = [block copy];
  661. });
  662. }
  663. - (TMDiskCacheObjectBlock)willRemoveObjectBlock
  664. {
  665. __block TMDiskCacheObjectBlock block = nil;
  666. dispatch_sync(_queue, ^{
  667. block = _willRemoveObjectBlock;
  668. });
  669. return block;
  670. }
  671. - (void)setWillRemoveObjectBlock:(TMDiskCacheObjectBlock)block
  672. {
  673. __weak TMDiskCache *weakSelf = self;
  674. dispatch_async(_queue, ^{
  675. TMDiskCache *strongSelf = weakSelf;
  676. if (!strongSelf)
  677. return;
  678. strongSelf->_willRemoveObjectBlock = [block copy];
  679. });
  680. }
  681. - (TMDiskCacheBlock)willRemoveAllObjectsBlock
  682. {
  683. __block TMDiskCacheBlock block = nil;
  684. dispatch_sync(_queue, ^{
  685. block = _willRemoveAllObjectsBlock;
  686. });
  687. return block;
  688. }
  689. - (void)setWillRemoveAllObjectsBlock:(TMDiskCacheBlock)block
  690. {
  691. __weak TMDiskCache *weakSelf = self;
  692. dispatch_async(_queue, ^{
  693. TMDiskCache *strongSelf = weakSelf;
  694. if (!strongSelf)
  695. return;
  696. strongSelf->_willRemoveAllObjectsBlock = [block copy];
  697. });
  698. }
  699. - (TMDiskCacheObjectBlock)didAddObjectBlock
  700. {
  701. __block TMDiskCacheObjectBlock block = nil;
  702. dispatch_sync(_queue, ^{
  703. block = _didAddObjectBlock;
  704. });
  705. return block;
  706. }
  707. - (void)setDidAddObjectBlock:(TMDiskCacheObjectBlock)block
  708. {
  709. __weak TMDiskCache *weakSelf = self;
  710. dispatch_async(_queue, ^{
  711. TMDiskCache *strongSelf = weakSelf;
  712. if (!strongSelf)
  713. return;
  714. strongSelf->_didAddObjectBlock = [block copy];
  715. });
  716. }
  717. - (TMDiskCacheObjectBlock)didRemoveObjectBlock
  718. {
  719. __block TMDiskCacheObjectBlock block = nil;
  720. dispatch_sync(_queue, ^{
  721. block = _didRemoveObjectBlock;
  722. });
  723. return block;
  724. }
  725. - (void)setDidRemoveObjectBlock:(TMDiskCacheObjectBlock)block
  726. {
  727. __weak TMDiskCache *weakSelf = self;
  728. dispatch_async(_queue, ^{
  729. TMDiskCache *strongSelf = weakSelf;
  730. if (!strongSelf)
  731. return;
  732. strongSelf->_didRemoveObjectBlock = [block copy];
  733. });
  734. }
  735. - (TMDiskCacheBlock)didRemoveAllObjectsBlock
  736. {
  737. __block TMDiskCacheBlock block = nil;
  738. dispatch_sync(_queue, ^{
  739. block = _didRemoveAllObjectsBlock;
  740. });
  741. return block;
  742. }
  743. - (void)setDidRemoveAllObjectsBlock:(TMDiskCacheBlock)block
  744. {
  745. __weak TMDiskCache *weakSelf = self;
  746. dispatch_async(_queue, ^{
  747. TMDiskCache *strongSelf = weakSelf;
  748. if (!strongSelf)
  749. return;
  750. strongSelf->_didRemoveAllObjectsBlock = [block copy];
  751. });
  752. }
  753. - (NSUInteger)byteLimit
  754. {
  755. __block NSUInteger byteLimit = 0;
  756. dispatch_sync(_queue, ^{
  757. byteLimit = _byteLimit;
  758. });
  759. return byteLimit;
  760. }
  761. - (void)setByteLimit:(NSUInteger)byteLimit
  762. {
  763. __weak TMDiskCache *weakSelf = self;
  764. dispatch_barrier_async(_queue, ^{
  765. TMDiskCache *strongSelf = weakSelf;
  766. if (!strongSelf)
  767. return;
  768. strongSelf->_byteLimit = byteLimit;
  769. if (byteLimit > 0)
  770. [strongSelf trimDiskToSizeByDate:byteLimit];
  771. });
  772. }
  773. - (NSTimeInterval)ageLimit
  774. {
  775. __block NSTimeInterval ageLimit = 0.0;
  776. dispatch_sync(_queue, ^{
  777. ageLimit = _ageLimit;
  778. });
  779. return ageLimit;
  780. }
  781. - (void)setAgeLimit:(NSTimeInterval)ageLimit
  782. {
  783. __weak TMDiskCache *weakSelf = self;
  784. dispatch_barrier_async(_queue, ^{
  785. TMDiskCache *strongSelf = weakSelf;
  786. if (!strongSelf)
  787. return;
  788. strongSelf->_ageLimit = ageLimit;
  789. [strongSelf trimToAgeLimitRecursively];
  790. });
  791. }
  792. #pragma mark - Background Tasks -
  793. + (void)setBackgroundTaskManager:(id <TMCacheBackgroundTaskManager>)backgroundTaskManager {
  794. TMCacheBackgroundTaskManager = backgroundTaskManager;
  795. }
  796. @end