Geen omschrijving

AWSTMDiskCache.m 30KB

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