PageRenderTime 27ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/src/Three20Network/Sources/TTURLCache.m

https://github.com/GetMoPix/three20
Objective C | 708 lines | 445 code | 161 blank | 102 comment | 67 complexity | 7608a7d03177e31bf70e413878e5db04 MD5 | raw file
  1. //
  2. // Copyright 2009-2011 Facebook
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. //
  16. #import "Three20Network/TTURLCache.h"
  17. // Network
  18. #import "Three20Network/TTGlobalNetwork.h"
  19. // Core
  20. #import "Three20Core/TTCorePreprocessorMacros.h"
  21. #import "Three20Core/TTGlobalCorePaths.h"
  22. #import "Three20Core/TTDebug.h"
  23. #import "Three20Core/TTDebugFlags.h"
  24. #import "Three20Core/NSStringAdditions.h"
  25. static const CGFloat kLargeImageSize = 600.0f * 400.0f;
  26. static NSString* kDefaultCacheName = @"Three20";
  27. static NSString* kEtagCacheDirectoryName = @"etag";
  28. static TTURLCache* gSharedCache = nil;
  29. static NSMutableDictionary* gNamedCaches = nil;
  30. ///////////////////////////////////////////////////////////////////////////////////////////////////
  31. ///////////////////////////////////////////////////////////////////////////////////////////////////
  32. ///////////////////////////////////////////////////////////////////////////////////////////////////
  33. @interface TTURLCache()
  34. /**
  35. * Creates paths as necessary and returns the cache path for the given name.
  36. */
  37. + (NSString*)cachePathWithName:(NSString*)name;
  38. @end
  39. ///////////////////////////////////////////////////////////////////////////////////////////////////
  40. ///////////////////////////////////////////////////////////////////////////////////////////////////
  41. ///////////////////////////////////////////////////////////////////////////////////////////////////
  42. @implementation TTURLCache
  43. @synthesize disableDiskCache = _disableDiskCache;
  44. @synthesize disableImageCache = _disableImageCache;
  45. @synthesize cachePath = _cachePath;
  46. @synthesize maxPixelCount = _maxPixelCount;
  47. @synthesize invalidationAge = _invalidationAge;
  48. ///////////////////////////////////////////////////////////////////////////////////////////////////
  49. - (id)initWithName:(NSString*)name {
  50. self = [super init];
  51. if (self) {
  52. _name = [name copy];
  53. _cachePath = [[TTURLCache cachePathWithName:name] retain];
  54. _invalidationAge = TT_DEFAULT_CACHE_INVALIDATION_AGE;
  55. // XXXjoe Disabling the built-in cache may save memory but it also makes UIWebView slow
  56. // NSURLCache* sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0
  57. // diskPath:nil];
  58. // [NSURLCache setSharedURLCache:sharedCache];
  59. // [sharedCache release];
  60. [[NSNotificationCenter defaultCenter]
  61. addObserver: self
  62. selector: @selector(didReceiveMemoryWarning:)
  63. name: UIApplicationDidReceiveMemoryWarningNotification
  64. object: nil];
  65. }
  66. return self;
  67. }
  68. ///////////////////////////////////////////////////////////////////////////////////////////////////
  69. - (id)init {
  70. self = [self initWithName:kDefaultCacheName];
  71. if (self) {
  72. }
  73. return self;
  74. }
  75. ///////////////////////////////////////////////////////////////////////////////////////////////////
  76. - (void)dealloc {
  77. [[NSNotificationCenter defaultCenter]
  78. removeObserver: self
  79. name: UIApplicationDidReceiveMemoryWarningNotification
  80. object: nil];
  81. TT_RELEASE_SAFELY(_name);
  82. TT_RELEASE_SAFELY(_imageCache);
  83. TT_RELEASE_SAFELY(_imageSortedList);
  84. TT_RELEASE_SAFELY(_cachePath);
  85. [super dealloc];
  86. }
  87. ///////////////////////////////////////////////////////////////////////////////////////////////////
  88. ///////////////////////////////////////////////////////////////////////////////////////////////////
  89. #pragma mark -
  90. #pragma mark Public
  91. ///////////////////////////////////////////////////////////////////////////////////////////////////
  92. + (TTURLCache*)cacheWithName:(NSString*)name {
  93. if (nil == gNamedCaches) {
  94. gNamedCaches = [[NSMutableDictionary alloc] init];
  95. }
  96. TTURLCache* cache = [gNamedCaches objectForKey:name];
  97. if (nil == cache) {
  98. cache = [[[TTURLCache alloc] initWithName:name] autorelease];
  99. [gNamedCaches setObject:cache forKey:name];
  100. }
  101. return cache;
  102. }
  103. ///////////////////////////////////////////////////////////////////////////////////////////////////
  104. + (TTURLCache*)sharedCache {
  105. if (nil == gSharedCache) {
  106. gSharedCache = [[TTURLCache alloc] init];
  107. }
  108. return gSharedCache;
  109. }
  110. ///////////////////////////////////////////////////////////////////////////////////////////////////
  111. + (void)setSharedCache:(TTURLCache*)cache {
  112. if (gSharedCache != cache) {
  113. [gSharedCache release];
  114. gSharedCache = [cache retain];
  115. }
  116. }
  117. ///////////////////////////////////////////////////////////////////////////////////////////////////
  118. + (BOOL)createPathIfNecessary:(NSString*)path {
  119. BOOL succeeded = YES;
  120. NSFileManager* fm = [NSFileManager defaultManager];
  121. if (![fm fileExistsAtPath:path]) {
  122. succeeded = [fm createDirectoryAtPath: path
  123. withIntermediateDirectories: YES
  124. attributes: nil
  125. error: nil];
  126. }
  127. return succeeded;
  128. }
  129. ///////////////////////////////////////////////////////////////////////////////////////////////////
  130. + (NSString*)cachePathWithName:(NSString*)name {
  131. NSArray* paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
  132. NSString* cachesPath = [paths objectAtIndex:0];
  133. NSString* cachePath = [cachesPath stringByAppendingPathComponent:name];
  134. NSString* etagCachePath = [cachePath stringByAppendingPathComponent:kEtagCacheDirectoryName];
  135. [self createPathIfNecessary:cachesPath];
  136. [self createPathIfNecessary:cachePath];
  137. [self createPathIfNecessary:etagCachePath];
  138. return cachePath;
  139. }
  140. ///////////////////////////////////////////////////////////////////////////////////////////////////
  141. ///////////////////////////////////////////////////////////////////////////////////////////////////
  142. #pragma mark -
  143. #pragma mark Private
  144. ///////////////////////////////////////////////////////////////////////////////////////////////////
  145. - (void)expireImagesFromMemory {
  146. while (_imageSortedList.count) {
  147. NSString* key = [_imageSortedList objectAtIndex:0];
  148. UIImage* image = [_imageCache objectForKey:key];
  149. TTDCONDITIONLOG(TTDFLAG_URLCACHE, @"EXPIRING %@", key);
  150. _totalPixelCount -= image.size.width * image.size.height;
  151. [_imageCache removeObjectForKey:key];
  152. [_imageSortedList removeObjectAtIndex:0];
  153. if (_totalPixelCount <= _maxPixelCount) {
  154. break;
  155. }
  156. }
  157. }
  158. ///////////////////////////////////////////////////////////////////////////////////////////////////
  159. - (void)storeImage:(UIImage*)image forURL:(NSString*)URL force:(BOOL)force {
  160. if (nil != image && (force || !_disableImageCache)) {
  161. int pixelCount = image.size.width * image.size.height;
  162. if (force || pixelCount < kLargeImageSize) {
  163. UIImage* existingImage = [_imageCache objectForKey:URL];
  164. if (nil != existingImage) {
  165. _totalPixelCount -= existingImage.size.width * existingImage.size.height;
  166. [_imageSortedList removeObject:URL];
  167. }
  168. _totalPixelCount += pixelCount;
  169. if (_totalPixelCount > _maxPixelCount && _maxPixelCount) {
  170. [self expireImagesFromMemory];
  171. }
  172. if (nil == _imageCache) {
  173. _imageCache = [[NSMutableDictionary alloc] init];
  174. }
  175. if (nil == _imageSortedList) {
  176. _imageSortedList = [[NSMutableArray alloc] init];
  177. }
  178. [_imageSortedList addObject:URL];
  179. [_imageCache setObject:image forKey:URL];
  180. }
  181. }
  182. }
  183. ///////////////////////////////////////////////////////////////////////////////////////////////////
  184. /**
  185. * TODO (jverkoey May 3, 2010): Clean up this redundant code.
  186. */
  187. - (BOOL)imageExistsFromBundle:(NSString*)URL {
  188. NSString* path = TTPathForBundleResource([URL substringFromIndex:9]);
  189. NSFileManager* fm = [NSFileManager defaultManager];
  190. return [fm fileExistsAtPath:path];
  191. }
  192. ///////////////////////////////////////////////////////////////////////////////////////////////////
  193. - (BOOL)imageExistsFromDocuments:(NSString*)URL {
  194. NSString* path = TTPathForDocumentsResource([URL substringFromIndex:12]);
  195. NSFileManager* fm = [NSFileManager defaultManager];
  196. return [fm fileExistsAtPath:path];
  197. }
  198. ///////////////////////////////////////////////////////////////////////////////////////////////////
  199. - (UIImage*)loadImageFromBundle:(NSString*)URL {
  200. NSString* path = TTPathForBundleResource([URL substringFromIndex:9]);
  201. return [UIImage imageWithContentsOfFile:path];
  202. }
  203. ///////////////////////////////////////////////////////////////////////////////////////////////////
  204. - (UIImage*)loadImageFromDocuments:(NSString*)URL {
  205. NSString* path = TTPathForDocumentsResource([URL substringFromIndex:12]);
  206. return [UIImage imageWithContentsOfFile:path];
  207. }
  208. ///////////////////////////////////////////////////////////////////////////////////////////////////
  209. - (NSString*)loadEtagFromCacheWithKey:(NSString*)key {
  210. NSString* path = [self etagCachePathForKey:key];
  211. NSData* data = [NSData dataWithContentsOfFile:path];
  212. return [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
  213. }
  214. ///////////////////////////////////////////////////////////////////////////////////////////////////
  215. - (NSString*)createTemporaryURL {
  216. static int temporaryURLIncrement = 0;
  217. return [NSString stringWithFormat:@"temp:%d", temporaryURLIncrement++];
  218. }
  219. ///////////////////////////////////////////////////////////////////////////////////////////////////
  220. - (NSString*)createUniqueTemporaryURL {
  221. NSFileManager* fm = [NSFileManager defaultManager];
  222. NSString* tempURL = nil;
  223. NSString* newPath = nil;
  224. do {
  225. tempURL = [self createTemporaryURL];
  226. newPath = [self cachePathForURL:tempURL];
  227. } while ([fm fileExistsAtPath:newPath]);
  228. return tempURL;
  229. }
  230. ///////////////////////////////////////////////////////////////////////////////////////////////////
  231. ///////////////////////////////////////////////////////////////////////////////////////////////////
  232. #pragma mark -
  233. #pragma mark NSNotifications
  234. ///////////////////////////////////////////////////////////////////////////////////////////////////
  235. - (void)didReceiveMemoryWarning:(void*)object {
  236. // Empty the memory cache when memory is low
  237. [self removeAll:NO];
  238. }
  239. ///////////////////////////////////////////////////////////////////////////////////////////////////
  240. ///////////////////////////////////////////////////////////////////////////////////////////////////
  241. #pragma mark -
  242. #pragma mark Public
  243. ///////////////////////////////////////////////////////////////////////////////////////////////////
  244. - (NSString*)etagCachePath {
  245. return [self.cachePath stringByAppendingPathComponent:kEtagCacheDirectoryName];
  246. }
  247. ///////////////////////////////////////////////////////////////////////////////////////////////////
  248. - (NSString *)keyForURL:(NSString*)URL {
  249. return [URL md5Hash];
  250. }
  251. ///////////////////////////////////////////////////////////////////////////////////////////////////
  252. - (NSString*)cachePathForURL:(NSString*)URL {
  253. NSString* key = [self keyForURL:URL];
  254. return [self cachePathForKey:key];
  255. }
  256. ///////////////////////////////////////////////////////////////////////////////////////////////////
  257. - (NSString*)cachePathForKey:(NSString*)key {
  258. return [_cachePath stringByAppendingPathComponent:key];
  259. }
  260. ///////////////////////////////////////////////////////////////////////////////////////////////////
  261. - (NSString*)etagCachePathForKey:(NSString*)key {
  262. return [self.etagCachePath stringByAppendingPathComponent:key];
  263. }
  264. ///////////////////////////////////////////////////////////////////////////////////////////////////
  265. - (BOOL)hasDataForURL:(NSString*)URL {
  266. NSString* filePath = [self cachePathForURL:URL];
  267. NSFileManager* fm = [NSFileManager defaultManager];
  268. return [fm fileExistsAtPath:filePath];
  269. }
  270. ///////////////////////////////////////////////////////////////////////////////////////////////////
  271. - (NSData*)dataForURL:(NSString*)URL {
  272. return [self dataForURL:URL expires:TT_CACHE_EXPIRATION_AGE_NEVER timestamp:nil];
  273. }
  274. ///////////////////////////////////////////////////////////////////////////////////////////////////
  275. - (NSData*)dataForURL:(NSString*)URL expires:(NSTimeInterval)expirationAge
  276. timestamp:(NSDate**)timestamp {
  277. NSString* key = [self keyForURL:URL];
  278. return [self dataForKey:key expires:expirationAge timestamp:timestamp];
  279. }
  280. ///////////////////////////////////////////////////////////////////////////////////////////////////
  281. - (BOOL)hasDataForKey:(NSString*)key expires:(NSTimeInterval)expirationAge {
  282. NSString* filePath = [self cachePathForKey:key];
  283. NSFileManager* fm = [NSFileManager defaultManager];
  284. if ([fm fileExistsAtPath:filePath]) {
  285. NSDictionary* attrs = [fm attributesOfItemAtPath:filePath error:nil];
  286. NSDate* modified = [attrs objectForKey:NSFileModificationDate];
  287. if ([modified timeIntervalSinceNow] < -expirationAge) {
  288. return NO;
  289. }
  290. return YES;
  291. }
  292. return NO;
  293. }
  294. ///////////////////////////////////////////////////////////////////////////////////////////////////
  295. - (NSData*)dataForKey:(NSString*)key expires:(NSTimeInterval)expirationAge
  296. timestamp:(NSDate**)timestamp {
  297. NSString* filePath = [self cachePathForKey:key];
  298. NSFileManager* fm = [NSFileManager defaultManager];
  299. if ([fm fileExistsAtPath:filePath]) {
  300. NSDictionary* attrs = [fm attributesOfItemAtPath:filePath error:nil];
  301. NSDate* modified = [attrs objectForKey:NSFileModificationDate];
  302. if ([modified timeIntervalSinceNow] < -expirationAge) {
  303. return nil;
  304. }
  305. if (timestamp) {
  306. *timestamp = modified;
  307. }
  308. return [NSData dataWithContentsOfFile:filePath];
  309. }
  310. return nil;
  311. }
  312. ///////////////////////////////////////////////////////////////////////////////////////////////////
  313. /**
  314. * This method needs to handle urlPaths with and without extensions.
  315. * So @"path.png" will resolve to @"path@2x.png" and
  316. * @"path" will resolve to @"path@2x"
  317. *
  318. * Paths beginning with @"." will not be changed.
  319. */
  320. + (NSString*)doubleImageURLPath:(NSString*)urlPath {
  321. if ([[urlPath substringToIndex:1] isEqualToString:@"."]) {
  322. return urlPath;
  323. }
  324. // We'd ideally use stringByAppendingPathExtension: in this method, but it seems
  325. // to wreck bundle:// urls by replacing them with bundle:/ prefixes. Strange.
  326. NSString* pathExtension = [urlPath pathExtension];
  327. NSString* urlPathWithNoExtension = [urlPath substringToIndex:
  328. [urlPath length] - [pathExtension length]
  329. - (([pathExtension length] > 0) ? 1 : 0)];
  330. urlPath = [urlPathWithNoExtension stringByAppendingString:@"@2x"];
  331. if ([pathExtension length] > 0) {
  332. urlPath = [urlPath stringByAppendingFormat:@".%@", pathExtension];
  333. }
  334. return urlPath;
  335. }
  336. ///////////////////////////////////////////////////////////////////////////////////////////////////
  337. - (BOOL)hasImageForURL:(NSString*)URL fromDisk:(BOOL)fromDisk {
  338. BOOL hasImage = (nil != [_imageCache objectForKey:URL]);
  339. if (!hasImage && fromDisk) {
  340. if (TTIsBundleURL(URL)) {
  341. hasImage = [self imageExistsFromBundle:URL];
  342. if (!hasImage) {
  343. hasImage = [self imageExistsFromBundle:[TTURLCache doubleImageURLPath:URL]];
  344. }
  345. } else if (TTIsDocumentsURL(URL)) {
  346. hasImage = [self imageExistsFromDocuments:URL];
  347. if (!hasImage) {
  348. hasImage = [self imageExistsFromDocuments:[TTURLCache doubleImageURLPath:URL]];
  349. }
  350. }
  351. }
  352. return hasImage;
  353. }
  354. ///////////////////////////////////////////////////////////////////////////////////////////////////
  355. - (id)imageForURL:(NSString*)URL {
  356. return [self imageForURL:URL fromDisk:YES];
  357. }
  358. ///////////////////////////////////////////////////////////////////////////////////////////////////
  359. - (id)imageForURL:(NSString*)URL fromDisk:(BOOL)fromDisk {
  360. UIImage* image = [_imageCache objectForKey:URL];
  361. if (nil == image && fromDisk) {
  362. if (TTIsBundleURL(URL)) {
  363. image = [self loadImageFromBundle:URL];
  364. [self storeImage:image forURL:URL];
  365. } else if (TTIsDocumentsURL(URL)) {
  366. image = [self loadImageFromDocuments:URL];
  367. [self storeImage:image forURL:URL];
  368. }
  369. }
  370. return image;
  371. }
  372. ///////////////////////////////////////////////////////////////////////////////////////////////////
  373. - (NSString*)etagForKey:(NSString*)key {
  374. return [self loadEtagFromCacheWithKey:key];
  375. }
  376. ///////////////////////////////////////////////////////////////////////////////////////////////////
  377. - (void)storeData:(NSData*)data forURL:(NSString*)URL {
  378. NSString* key = [self keyForURL:URL];
  379. [self storeData:data forKey:key];
  380. }
  381. ///////////////////////////////////////////////////////////////////////////////////////////////////
  382. - (void)storeData:(NSData*)data forKey:(NSString*)key {
  383. if (!_disableDiskCache) {
  384. NSString* filePath = [self cachePathForKey:key];
  385. NSFileManager* fm = [NSFileManager defaultManager];
  386. [fm createFileAtPath:filePath contents:data attributes:nil];
  387. }
  388. }
  389. ///////////////////////////////////////////////////////////////////////////////////////////////////
  390. - (void)storeImage:(UIImage*)image forURL:(NSString*)URL {
  391. [self storeImage:image forURL:URL force:NO];
  392. }
  393. ///////////////////////////////////////////////////////////////////////////////////////////////////
  394. - (void)storeEtag:(NSString*)etag forKey:(NSString*)key {
  395. NSString* filePath = [self etagCachePathForKey:key];
  396. NSFileManager* fm = [NSFileManager defaultManager];
  397. [fm createFileAtPath: filePath
  398. contents: [etag dataUsingEncoding:NSUTF8StringEncoding]
  399. attributes: nil];
  400. }
  401. ///////////////////////////////////////////////////////////////////////////////////////////////////
  402. - (NSString*)storeTemporaryData:(NSData*)data {
  403. NSString* URL = [self createUniqueTemporaryURL];
  404. [self storeData:data forURL:URL];
  405. return URL;
  406. }
  407. ///////////////////////////////////////////////////////////////////////////////////////////////////
  408. - (NSString*)storeTemporaryFile:(NSURL*)fileURL {
  409. if ([fileURL isFileURL]) {
  410. NSString* filePath = [fileURL path];
  411. NSFileManager* fm = [NSFileManager defaultManager];
  412. if ([fm fileExistsAtPath:filePath]) {
  413. NSString* tempURL = nil;
  414. NSString* newPath = nil;
  415. do {
  416. tempURL = [self createTemporaryURL];
  417. newPath = [self cachePathForURL:tempURL];
  418. } while ([fm fileExistsAtPath:newPath]);
  419. if ([fm moveItemAtPath:filePath toPath:newPath error:nil]) {
  420. return tempURL;
  421. }
  422. }
  423. }
  424. return nil;
  425. }
  426. ///////////////////////////////////////////////////////////////////////////////////////////////////
  427. - (NSString*)storeTemporaryImage:(UIImage*)image toDisk:(BOOL)toDisk {
  428. NSString* URL = [self createUniqueTemporaryURL];
  429. [self storeImage:image forURL:URL force:YES];
  430. NSData* data = UIImagePNGRepresentation(image);
  431. [self storeData:data forURL:URL];
  432. return URL;
  433. }
  434. ///////////////////////////////////////////////////////////////////////////////////////////////////
  435. - (void)moveDataForURL:(NSString*)oldURL toURL:(NSString*)newURL {
  436. id image = [self imageForURL:oldURL];
  437. if (image) {
  438. [_imageSortedList removeObject:oldURL];
  439. [_imageCache removeObjectForKey:oldURL];
  440. [_imageSortedList addObject:newURL];
  441. [_imageCache setObject:image forKey:newURL];
  442. }
  443. NSString* oldKey = [self keyForURL:oldURL];
  444. NSString* oldPath = [self cachePathForKey:oldKey];
  445. NSFileManager* fm = [NSFileManager defaultManager];
  446. if ([fm fileExistsAtPath:oldPath]) {
  447. NSString* newKey = [self keyForURL:newURL];
  448. NSString* newPath = [self cachePathForKey:newKey];
  449. [fm moveItemAtPath:oldPath toPath:newPath error:nil];
  450. }
  451. }
  452. ///////////////////////////////////////////////////////////////////////////////////////////////////
  453. - (void)moveDataFromPath:(NSString*)path toURL:(NSString*)newURL {
  454. NSString* newKey = [self keyForURL:newURL];
  455. NSFileManager* fm = [NSFileManager defaultManager];
  456. if ([fm fileExistsAtPath:path]) {
  457. NSString* newPath = [self cachePathForKey:newKey];
  458. [fm moveItemAtPath:path toPath:newPath error:nil];
  459. }
  460. }
  461. ///////////////////////////////////////////////////////////////////////////////////////////////////
  462. - (NSString*)moveDataFromPathToTemporaryURL:(NSString*)path {
  463. NSString* tempURL = [self createUniqueTemporaryURL];
  464. [self moveDataFromPath:path toURL:tempURL];
  465. return tempURL;
  466. }
  467. ///////////////////////////////////////////////////////////////////////////////////////////////////
  468. - (void)removeURL:(NSString*)URL fromDisk:(BOOL)fromDisk {
  469. [_imageSortedList removeObject:URL];
  470. [_imageCache removeObjectForKey:URL];
  471. if (fromDisk) {
  472. NSString* key = [self keyForURL:URL];
  473. NSString* filePath = [self cachePathForKey:key];
  474. NSFileManager* fm = [NSFileManager defaultManager];
  475. if (filePath && [fm fileExistsAtPath:filePath]) {
  476. [fm removeItemAtPath:filePath error:nil];
  477. }
  478. }
  479. }
  480. ///////////////////////////////////////////////////////////////////////////////////////////////////
  481. - (void)removeKey:(NSString*)key {
  482. NSString* filePath = [self cachePathForKey:key];
  483. NSFileManager* fm = [NSFileManager defaultManager];
  484. if (filePath && [fm fileExistsAtPath:filePath]) {
  485. [fm removeItemAtPath:filePath error:nil];
  486. }
  487. }
  488. ///////////////////////////////////////////////////////////////////////////////////////////////////
  489. - (void)removeAll:(BOOL)fromDisk {
  490. [_imageCache removeAllObjects];
  491. [_imageSortedList removeAllObjects];
  492. _totalPixelCount = 0;
  493. if (fromDisk) {
  494. NSFileManager* fm = [NSFileManager defaultManager];
  495. [fm removeItemAtPath:_cachePath error:nil];
  496. [TTURLCache createPathIfNecessary:_cachePath];
  497. }
  498. }
  499. ///////////////////////////////////////////////////////////////////////////////////////////////////
  500. - (void)invalidateURL:(NSString*)URL {
  501. NSString* key = [self keyForURL:URL];
  502. return [self invalidateKey:key];
  503. }
  504. ///////////////////////////////////////////////////////////////////////////////////////////////////
  505. - (void)invalidateKey:(NSString*)key {
  506. NSString* filePath = [self cachePathForKey:key];
  507. NSFileManager* fm = [NSFileManager defaultManager];
  508. if (filePath && [fm fileExistsAtPath:filePath]) {
  509. NSDate* invalidDate = [NSDate dateWithTimeIntervalSinceNow:-_invalidationAge];
  510. NSDictionary* attrs = [NSDictionary dictionaryWithObject:invalidDate
  511. forKey:NSFileModificationDate];
  512. #if __IPHONE_4_0 <= __IPHONE_OS_VERSION_MAX_ALLOWED
  513. [fm setAttributes:attrs ofItemAtPath:filePath error:nil];
  514. #else
  515. [fm changeFileAttributes:attrs atPath:filePath];
  516. #endif
  517. }
  518. }
  519. ///////////////////////////////////////////////////////////////////////////////////////////////////
  520. - (void)invalidateAll {
  521. NSDate* invalidDate = [NSDate dateWithTimeIntervalSinceNow:-_invalidationAge];
  522. NSDictionary* attrs = [NSDictionary dictionaryWithObject:invalidDate
  523. forKey:NSFileModificationDate];
  524. NSFileManager* fm = [NSFileManager defaultManager];
  525. NSDirectoryEnumerator* e = [fm enumeratorAtPath:_cachePath];
  526. for (NSString* fileName; fileName = [e nextObject]; ) {
  527. NSString* filePath = [_cachePath stringByAppendingPathComponent:fileName];
  528. #if __IPHONE_4_0 <= __IPHONE_OS_VERSION_MAX_ALLOWED
  529. [fm setAttributes:attrs ofItemAtPath:filePath error:nil];
  530. #else
  531. [fm changeFileAttributes:attrs atPath:filePath];
  532. #endif
  533. }
  534. }
  535. ///////////////////////////////////////////////////////////////////////////////////////////////////
  536. - (void)logMemoryUsage {
  537. #if TTLOGLEVEL_INFO <= TTMAXLOGLEVEL
  538. TTDCONDITIONLOG(TTDFLAG_URLCACHE, @"======= IMAGE CACHE: %d images, %d pixels ========",
  539. _imageCache.count, _totalPixelCount);
  540. NSEnumerator* e = [_imageCache keyEnumerator];
  541. for (NSString* key ; key = [e nextObject]; ) {
  542. UIImage* image = [_imageCache objectForKey:key];
  543. TTDCONDITIONLOG(TTDFLAG_URLCACHE, @" %f x %f %@", image.size.width, image.size.height, key);
  544. }
  545. #endif
  546. }
  547. @end