1
0
mirror of https://github.com/danog/Telegram.git synced 2024-12-04 10:27:46 +01:00
Telegram/legacy/TelegraphKit/TGCache.m
2015-10-01 19:19:52 +03:00

814 lines
27 KiB
Objective-C

#import "TGCache.h"
#import <objc/runtime.h>
#import <CommonCrypto/CommonDigest.h>
#import <ImageIO/ImageIO.h>
#import <pthread.h>
#import "TGImageUtils.h"
#import "TGAppDelegate.h"
#define TG_SYNCHRONIZED_DEFINE(lock) pthread_mutex_t TG_SYNCHRONIZED_##lock
#define TG_SYNCHRONIZED_INIT(lock) pthread_mutex_init(&TG_SYNCHRONIZED_##lock, NULL)
#define TG_SYNCHRONIZED_BEGIN(lock) pthread_mutex_lock(&TG_SYNCHRONIZED_##lock);
#define TG_SYNCHRONIZED_END(lock) pthread_mutex_unlock(&TG_SYNCHRONIZED_##lock);
static NSString *md5String(NSString *string)
{
/*static const char *md5PropertyKey = "MD5Key";
NSString *result = objc_getAssociatedObject(string, md5PropertyKey);
if (result != nil)
return result;*/
const char *ptr = [string UTF8String];
unsigned char md5Buffer[16];
CC_MD5(ptr, (CC_LONG)[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding], md5Buffer);
NSString *output = [[NSString alloc] initWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", md5Buffer[0], md5Buffer[1], md5Buffer[2], md5Buffer[3], md5Buffer[4], md5Buffer[5], md5Buffer[6], md5Buffer[7], md5Buffer[8], md5Buffer[9], md5Buffer[10], md5Buffer[11], md5Buffer[12], md5Buffer[13], md5Buffer[14], md5Buffer[15]];
//objc_setAssociatedObject(string, md5PropertyKey, output, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return output;
}
@interface TGCacheRecord : NSObject
@property (nonatomic) NSTimeInterval date;
@property (nonatomic, strong) id object;
@property (nonatomic) NSUInteger size;
- (id)initWithObject:(id)object size:(NSUInteger)size;
@end
@implementation TGCacheRecord
@synthesize date = _date;
@synthesize object = _object;
@synthesize size = _size;
- (id)initWithObject:(id)object size:(NSUInteger)size
{
self = [super init];
if (self != nil)
{
_object = object;
_date = CFAbsoluteTimeGetCurrent();
_size = size;
}
return self;
}
@end
static NSFileManager *cacheFileManager = nil;
@interface TGCache ()
{
TG_SYNCHRONIZED_DEFINE(_dataMemoryCache);
}
@property (nonatomic, strong) NSMutableArray *temporaryCachedImagesSources;
@property (nonatomic, strong) NSMutableDictionary *memoryCache;
@property (nonatomic) int memoryCacheSize1;
@property (nonatomic, strong) NSMutableDictionary *thumbnailCache;
@property (nonatomic) int thumbnailCacheSize;
@property (nonatomic, strong) NSMutableDictionary *dataMemoryCache;
@property (nonatomic) int dataMemoryCacheSize;
@end
@implementation TGCache
+ (dispatch_queue_t)diskCacheQueue
{
static dispatch_queue_t queue = NULL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
queue = dispatch_queue_create("com.telegraph.diskcache", 0);
//dispatch_set_target_queue(queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0));
if (cacheFileManager == nil)
cacheFileManager = [[NSFileManager alloc] init];
});
return queue;
}
+ (NSFileManager *)diskFileManager
{
if (cacheFileManager == nil)
cacheFileManager = [[NSFileManager alloc] init];
return cacheFileManager;
}
- (id)init
{
self = [super init];
if (self != nil)
{
TG_SYNCHRONIZED_INIT(_dataMemoryCache);
_imageMemoryLimit = deviceMemorySize() > 300 ? (int)(15 * 1024 * 1024) : (int)(11 * 1024 * 1024);
_imageMemoryEvictionInterval = deviceMemorySize() > 300 ? 1024 * 1024 : 812 * 1024;
//_imageMemoryLimit = 10;
//_imageMemoryEvictionInterval = 10;
_thumbnailMemoryLimit = deviceMemorySize() > 300 ? (int)(1.6 * 1024 * 1024) : (int)(1.1 * 1024 * 1024);
_thumbnailEvictionInterval = cpuCoreCount() > 1 ? (int)(0.4 * 1024 * 1024) : (int)(0.25 * 1024 * 1024);
_dataMemoryLimit = deviceMemorySize() > 300 ? (int)(1 * 1024 * 1024) : (int)(0.6 * 1024 * 1024);
_dataMemoryEvictionInterval = cpuCoreCount() > 1 ? (int)(0.4 * 1024 * 1024) : (int)(0.25 * 1024 * 1024);
_diskLimit = 32 * 1024 * 1024;
_diskEvictionInterval = 6 * 1024 * 1024;
_memoryWarningBaseline = deviceMemorySize() > 300 ? (int)(1.5 * 1024 * 1024) : (int)(1.1 * 1024 * 1024);
_backgroundBaseline = deviceMemorySize() > 300 ? (int)(5.8 * 1024 * 1024) : (int)(2.8 * 1024 * 1024);
_temporaryCachedImagesSources = [[NSMutableArray alloc] init];
_memoryCache = [[NSMutableDictionary alloc] init];
self.memoryCacheSize = 0;
_thumbnailCache = [[NSMutableDictionary alloc] init];
_thumbnailCacheSize = 0;
_dataMemoryCache = [[NSMutableDictionary alloc] init];
_dataMemoryCacheSize = 0;
NSString *cachesPath = [TGAppDelegate cachePath];
_diskCachePath = cachesPath;
NSFileManager *fileManager = [[NSFileManager alloc] init];
if (![fileManager fileExistsAtPath:_diskCachePath])
[fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
}
return self;
}
- (int)memoryCacheSize
{
return _memoryCacheSize1;
}
- (void)setMemoryCacheSize:(int)memoryCacheSize
{
_memoryCacheSize1 = memoryCacheSize;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)didReceiveMemoryWarning:(NSNotification *)__unused notification
{
[self freeMemoryCache:_memoryWarningBaseline];
}
- (void)didEnterBackground:(NSNotification *)__unused notification
{
[self freeMemoryCache:_backgroundBaseline];
}
- (void)addTemporaryCachedImagesSource:(NSDictionary *)source autoremove:(bool)autoremove
{
dispatch_block_t block = ^
{
[_temporaryCachedImagesSources addObject:source];
if (autoremove)
{
dispatch_async(dispatch_get_main_queue(), ^{
[self removeTemporaryCachedImageSource:source];
});
}
};
if ([NSThread isMainThread])
block();
else
dispatch_async(dispatch_get_main_queue(), block);
}
- (void)removeTemporaryCachedImageSource:(NSDictionary *)source
{
dispatch_block_t block = ^
{
[_temporaryCachedImagesSources removeObject:source];
};
if ([NSThread isMainThread])
block();
else
dispatch_async(dispatch_get_main_queue(), block);
}
- (void)freeMemoryCache:(NSUInteger)targetSize
{
dispatch_block_t block = ^
{
if (self.memoryCacheSize > (int)targetSize)
{
__unused int sizeBefore = self.memoryCacheSize;
NSArray *sortedKeys = [_memoryCache keysSortedByValueUsingComparator:^NSComparisonResult(id obj1, id obj2)
{
return (((TGCacheRecord *)obj1).date < ((TGCacheRecord *)obj2).date) ? NSOrderedAscending : NSOrderedDescending;
}];
for (int i = 0; i < (int)sortedKeys.count && self.memoryCacheSize > (int)targetSize; i++)
{
NSString *key = [sortedKeys objectAtIndex:i];
TGCacheRecord *record = [_memoryCache objectForKey:key];
self.memoryCacheSize -= (int)record.size;
if (self.memoryCacheSize < 0)
self.memoryCacheSize = 0;
[_memoryCache removeObjectForKey:key];
//TGLog(@"evict %@", key);
}
__block int currentCacheSize = 0;
[_memoryCache enumerateKeysAndObjectsUsingBlock:^(__unused NSString *key, TGCacheRecord *record, __unused BOOL *stop)
{
currentCacheSize += record.size;
}];
self.memoryCacheSize = currentCacheSize;
//TGLog(@"TGCache: freed %d kbytes (cache size: %d kbytes)", (int)((sizeBefore - self.memoryCacheSize) / 1024), (int)(self.memoryCacheSize / 1024));
}
};
if ([NSThread isMainThread])
block();
else
dispatch_async(dispatch_get_main_queue(), block);
}
- (void)freeThumbnailCache:(NSUInteger)targetSize
{
dispatch_block_t block = ^
{
if (_thumbnailCacheSize > (int)targetSize)
{
//int sizeBefore = _thumbnailCacheSize;
NSArray *sortedKeys = [_thumbnailCache keysSortedByValueUsingComparator:^NSComparisonResult(id obj1, id obj2)
{
return (((TGCacheRecord *)obj1).date < ((TGCacheRecord *)obj2).date) ? NSOrderedAscending : NSOrderedDescending;
}];
for (int i = 0; i < (int)sortedKeys.count && _thumbnailCacheSize > (int)targetSize; i++)
{
NSString *key = [sortedKeys objectAtIndex:i];
TGCacheRecord *record = [_thumbnailCache objectForKey:key];
_thumbnailCacheSize -= record.size;
if (_thumbnailCacheSize < 0)
_thumbnailCacheSize = 0;
[_thumbnailCache removeObjectForKey:key];
}
}
};
if ([NSThread isMainThread])
block();
else
dispatch_async(dispatch_get_main_queue(), block);
}
- (void)freeCompressedMemoryCache:(NSUInteger)targetSize reentrant:(bool)reentrant
{
if (!reentrant)
TG_SYNCHRONIZED_BEGIN(_dataMemoryCache);
{
if (_dataMemoryCacheSize > (int)targetSize)
{
__unused int sizeBefore = _dataMemoryCacheSize;
NSArray *sortedKeys = [_dataMemoryCache keysSortedByValueUsingComparator:^NSComparisonResult(id obj1, id obj2)
{
return (((TGCacheRecord *)obj1).date < ((TGCacheRecord *)obj2).date) ? NSOrderedAscending : NSOrderedDescending;
}];
for (int i = 0; i < (int)sortedKeys.count && _dataMemoryCacheSize > (int)targetSize; i++)
{
NSString *key = [sortedKeys objectAtIndex:i];
TGCacheRecord *record = [_dataMemoryCache objectForKey:key];
_dataMemoryCacheSize -= record.size;
if (_dataMemoryCacheSize < 0)
_dataMemoryCacheSize = 0;
[_dataMemoryCache removeObjectForKey:key];
}
//TGLog(@"TGCache (compressed): freed %d kbytes (cache size: %d kbytes)", (int)((sizeBefore - _dataMemoryCacheSize) / 1024), (int)(_dataMemoryCacheSize / 1024));
}
}
if (!reentrant)
TG_SYNCHRONIZED_END(_dataMemoryCache);
}
- (void)cacheCompressedObject:(NSData *)data url:(NSString *)url reentrant:(bool)reentrant
{
if (!reentrant)
TG_SYNCHRONIZED_BEGIN(_dataMemoryCache);
TGCacheRecord *cacheRecord = [_dataMemoryCache objectForKey:url];
if (cacheRecord != nil)
{
_dataMemoryCacheSize -= cacheRecord.size;
cacheRecord.date = CFAbsoluteTimeGetCurrent();
cacheRecord.object = data;
cacheRecord.size = data.length;
_dataMemoryCacheSize += cacheRecord.size;
}
else
{
[_dataMemoryCache setObject:[[TGCacheRecord alloc] initWithObject:data size:data.length] forKey:url];
_dataMemoryCacheSize += data.length;
}
if (_dataMemoryCacheSize >= _dataMemoryLimit + _dataMemoryEvictionInterval)
[self freeCompressedMemoryCache:_dataMemoryLimit reentrant:true];
if (!reentrant)
TG_SYNCHRONIZED_END(_dataMemoryCache);
}
- (void)cacheImage:(UIImage *)image withData:(NSData *)data url:(NSString *)url availability:(int)availability
{
[self cacheImage:image withData:data url:url availability:availability completion:nil];
}
- (void)cacheImage:(UIImage *)image withData:(NSData *)data url:(NSString *)url availability:(int)availability completion:(void (^)(void))completion
{
#ifdef DEBUG
if (data != nil)
TGLog(@"cache image %d bytes with url %@", (int)data.length, url);
#endif
if (image != nil && (availability & TGCacheMemory))
{
int size = (int)(image.size.width * image.size.height * 4 * image.scale);
dispatch_block_t block = ^
{
TGCacheRecord *cacheRecord = [_memoryCache objectForKey:url];
if (cacheRecord != nil)
{
self.memoryCacheSize -= (int)cacheRecord.size;
cacheRecord.date = CFAbsoluteTimeGetCurrent();
cacheRecord.object = image;
cacheRecord.size = size;
self.memoryCacheSize += size;
}
else
{
[_memoryCache setObject:[[TGCacheRecord alloc] initWithObject:image size:size] forKey:url];
self.memoryCacheSize += size;
}
if (self.memoryCacheSize >= _imageMemoryLimit + _imageMemoryEvictionInterval)
[self freeMemoryCache:_imageMemoryLimit];
};
if ([NSThread isMainThread])
block();
else
dispatch_async(dispatch_get_main_queue(), block);
}
if ((data != nil || image != nil) && (availability & TGCacheDisk) && url != nil)
{
dispatch_async([TGCache diskCacheQueue], ^
{
if (data != nil)
{
[self cacheCompressedObject:data url:url reentrant:false];
[data writeToFile:[_diskCachePath stringByAppendingPathComponent:md5String(url)] atomically:true];
if (completion != nil)
completion();
}
});
}
}
- (UIImage *)cachedImage:(NSString *)url availability:(int)availability
{
UIImage *image = nil;
if (availability & TGCacheMemory)
{
__block UIImage *blockImage = nil;
dispatch_block_t block = ^
{
TGCacheRecord *cacheRecord = [_memoryCache objectForKey:url];
if (cacheRecord != nil)
{
cacheRecord.date = CFAbsoluteTimeGetCurrent();
blockImage = cacheRecord.object;
}
else if (_temporaryCachedImagesSources.count != 0)
{
for (NSDictionary *dict in _temporaryCachedImagesSources)
{
UIImage *image = [dict objectForKey:url];
//TGLog(@"From temp cache %@", url);
if (image != nil)
{
blockImage = image;
break;
}
}
}
};
if ([NSThread isMainThread])
block();
else
dispatch_sync(dispatch_get_main_queue(), block);
image = blockImage;
}
if (image != nil)
return image;
if (availability & TGCacheDisk)
{
UIImage *dataImage = nil;
TG_SYNCHRONIZED_BEGIN(_dataMemoryCache);
{
TGCacheRecord *cacheRecord = [_dataMemoryCache objectForKey:url];
if (cacheRecord != nil)
{
cacheRecord.date = CFAbsoluteTimeGetCurrent();
if (false)
{
dataImage = [[UIImage alloc] initWithData:cacheRecord.object];
}
else
{
NSDictionary *dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:true] forKey:(id)kCGImageSourceShouldCache];
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)cacheRecord.object, nil);
if (source != nil)
{
CGImageRef cgImage = CGImageSourceCreateImageAtIndex(source, 0, (__bridge CFDictionaryRef)dict);
dataImage = [[UIImage alloc] initWithCGImage:cgImage];
CGImageRelease(cgImage);
CFRelease(source);
}
}
if (dataImage != nil && (availability & TGCacheMemory))
[self cacheImage:dataImage withData:nil url:url availability:TGCacheMemory];
}
}
TG_SYNCHRONIZED_END(_dataMemoryCache);
if (dataImage != nil)
return dataImage;
__block UIImage *diskImageResult = nil;
dispatch_sync([TGCache diskCacheQueue], ^
{
NSDictionary *dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:true] forKey:(id)kCGImageSourceShouldCache];
NSURL *realUrl = [[NSURL alloc] initFileURLWithPath:[_diskCachePath stringByAppendingPathComponent:md5String(url)]];
CGImageSourceRef source = CGImageSourceCreateWithURL((__bridge CFURLRef)realUrl, NULL);
if (source != nil)
{
CGImageRef cgImage = CGImageSourceCreateImageAtIndex(source, 0, (__bridge CFDictionaryRef)dict);
UIImage *diskImage = [UIImage imageWithCGImage:cgImage];
if (diskImage != nil && (availability & TGCacheMemory))
{
[self cacheImage:diskImage withData:nil url:url availability:TGCacheMemory];
}
if (diskImage != nil)
{
diskImageResult = diskImage;
if (diskImage != nil)
{
dispatch_async([TGCache diskCacheQueue], ^
{
NSData *data = [[NSData alloc] initWithContentsOfURL:realUrl];
if (data != nil)
{
[self cacheCompressedObject:data url:url reentrant:false];
}
});
}
}
CGImageRelease(cgImage);
CFRelease(source);
}
});
image = diskImageResult;
return image;
}
return nil;
}
- (void)removeFromMemoryCache:(NSString *)url matchEnd:(bool)matchEnd
{
if (url == nil)
return;
dispatch_block_t block = ^
{
TGCacheRecord *cacheRecord = [_memoryCache objectForKey:url];
if (cacheRecord != nil)
{
self.memoryCacheSize -= (int)cacheRecord.size;
if (self.memoryCacheSize < 0)
self.memoryCacheSize = 0;
[_memoryCache removeObjectForKey:url];
}
if (matchEnd)
{
NSMutableArray *removeKeys = [[NSMutableArray alloc] init];
[_memoryCache enumerateKeysAndObjectsUsingBlock:^(NSString *key, __unused id obj, __unused BOOL *stop)
{
if ([key hasSuffix:url])
[removeKeys addObject:key];
}];
[_memoryCache removeObjectsForKeys:removeKeys];
}
TGCacheRecord *dataCacheRecord = [_dataMemoryCache objectForKey:url];
if (dataCacheRecord != nil)
{
_dataMemoryCacheSize -= dataCacheRecord.size;
if (_dataMemoryCacheSize < 0)
_dataMemoryCacheSize = 0;
[_dataMemoryCache removeObjectForKey:url];
}
};
if ([NSThread isMainThread])
block();
else
dispatch_sync(dispatch_get_main_queue(), block);
}
- (void)cacheThumbnail:(UIImage *)image url:(NSString *)url
{
if ([self cachedThumbnail:url] != nil)
return;
if (image != nil)
{
int side = 32;
if (!TGIsRetina())
side *= 2;
int size = (int)(side * side * 4);
if (image.size.width > side || image.size.height > side)
{
image = TGScaleImage(image, CGSizeMake(side, side));
}
else
return;
dispatch_block_t block = ^
{
TGCacheRecord *cacheRecord = [_thumbnailCache objectForKey:url];
if (cacheRecord != nil)
{
_thumbnailCacheSize -= cacheRecord.size;
cacheRecord.date = CFAbsoluteTimeGetCurrent();
cacheRecord.object = image;
cacheRecord.size = size;
_thumbnailCacheSize += size;
}
else
{
[_thumbnailCache setObject:[[TGCacheRecord alloc] initWithObject:image size:size] forKey:url];
_thumbnailCacheSize += size;
}
//TGLog(@"Cache thumbnail: %@", url);
//TGLog(@"_thumbnailCacheSize = %d", _thumbnailCacheSize);
if (_thumbnailCacheSize >= _thumbnailMemoryLimit + _thumbnailEvictionInterval)
[self freeThumbnailCache:_thumbnailMemoryLimit];
};
if ([NSThread isMainThread])
block();
else
dispatch_async(dispatch_get_main_queue(), block);
}
}
- (UIImage *)cachedThumbnail:(NSString *)url
{
UIImage *image = nil;
__block UIImage *blockImage = nil;
dispatch_block_t block = ^
{
TGCacheRecord *cacheRecord = [_thumbnailCache objectForKey:url];
if (cacheRecord != nil)
{
cacheRecord.date = CFAbsoluteTimeGetCurrent();
blockImage = cacheRecord.object;
}
else
{
//TGLog(@"Thumbnail not found: %@", url);
}
};
if ([NSThread isMainThread])
block();
else
dispatch_async(dispatch_get_main_queue(), block);
image = blockImage;
if (image != nil)
return image;
return nil;
}
- (void)diskCacheContains:(NSString *)url1 orUrl:(NSString *)url2 completion:(void (^)(bool containsFirst, bool containsSecond))completion
{
dispatch_async([TGCache diskCacheQueue], ^
{
bool cached = [cacheFileManager fileExistsAtPath:[_diskCachePath stringByAppendingPathComponent:md5String(url1)]];
if (cached)
{
if (completion)
completion(true, false);
}
else if (url2 != nil)
{
cached = [cacheFileManager fileExistsAtPath:[_diskCachePath stringByAppendingPathComponent:md5String(url2)]];
if (completion)
completion(false, cached);
}
else
{
if (completion)
completion(false, false);
}
});
}
- (bool)diskCacheContainsSync:(NSString *)url
{
__block bool result = false;
dispatch_sync([TGCache diskCacheQueue], ^
{
result = [cacheFileManager fileExistsAtPath:[_diskCachePath stringByAppendingPathComponent:md5String(url)]];
});
return result;
}
- (void)removeFromDiskCache:(NSString *)url
{
dispatch_async([TGCache diskCacheQueue], ^
{
[cacheFileManager removeItemAtPath:[_diskCachePath stringByAppendingPathComponent:md5String(url)] error:nil];
});
}
- (NSString *)pathForCachedData:(NSString *)url
{
return [_diskCachePath stringByAppendingPathComponent:md5String(url)];
}
- (void)changeCacheItemUrl:(NSString *)oldUrl newUrl:(NSString *)newUrl
{
//TGLog(@"TGCache: rename \"%@\" -> \"%@\"", oldUrl, newUrl);
dispatch_block_t block = ^
{
TGCacheRecord *record = [_memoryCache objectForKey:oldUrl];
if (record != nil)
{
[_memoryCache setObject:record forKey:newUrl];
[_memoryCache removeObjectForKey:oldUrl];
}
};
if ([NSThread isMainThread])
block();
else
dispatch_async(dispatch_get_main_queue(), block);
dispatch_async([TGCache diskCacheQueue], ^
{
NSError *error = nil;
[cacheFileManager moveItemAtPath:[_diskCachePath stringByAppendingPathComponent:md5String(oldUrl)] toPath:[_diskCachePath stringByAppendingPathComponent:md5String(newUrl)] error:&error];
if (error != nil)
TGLog(@"Failed to move: %@", error);
});
}
- (void)moveToCache:(NSString *)fileUrl cacheUrl:(NSString *)cacheUrl
{
TGLog(@"TGCache: move \"%@\" -> \"%@\"", fileUrl, cacheUrl);
dispatch_block_t block = ^
{
TGCacheRecord *record = [_memoryCache objectForKey:fileUrl];
if (record != nil)
{
[_memoryCache setObject:record forKey:cacheUrl];
[_memoryCache removeObjectForKey:fileUrl];
}
};
if ([NSThread isMainThread])
block();
else
dispatch_async(dispatch_get_main_queue(), block);
dispatch_async([TGCache diskCacheQueue], ^
{
NSError *error = nil;
[cacheFileManager moveItemAtPath:fileUrl toPath:[_diskCachePath stringByAppendingPathComponent:md5String(cacheUrl)] error:&error];
if (error != nil)
TGLog(@"Failed to move: %@", error);
});
}
- (void)clearCache:(int)availability
{
if (availability & TGCacheMemory)
{
dispatch_block_t block = ^
{
[_memoryCache removeAllObjects];
self.memoryCacheSize = 0;
};
if ([NSThread isMainThread])
block();
else
dispatch_async(dispatch_get_main_queue(), block);
TG_SYNCHRONIZED_BEGIN(_dataMemoryCache);
[_dataMemoryCache removeAllObjects];
_dataMemoryCacheSize = 0;
TG_SYNCHRONIZED_END(_dataMemoryCache);
}
if (availability & TGCacheDisk)
{
dispatch_async([TGCache diskCacheQueue], ^
{
NSDirectoryEnumerator* en = [cacheFileManager enumeratorAtPath:_diskCachePath];
NSError* error = nil;
int removedCount = 0;
int failedCount = 0;
NSString* file;
while (file = [en nextObject])
{
if ([cacheFileManager removeItemAtPath:[_diskCachePath stringByAppendingPathComponent:file] error:&error])
removedCount++;
else
failedCount++;
}
TGLog(@"TGCache: removed %d files (%d failed)", removedCount, failedCount);
});
}
}
- (NSArray *)storeMemoryCache
{
NSMutableArray *array = [[NSMutableArray alloc] init];
dispatch_block_t block = ^
{
[_memoryCache enumerateKeysAndObjectsUsingBlock:^(NSString *key, __unused id obj, __unused BOOL *stop)
{
[array addObject:key];
}];
};
if ([NSThread isMainThread])
block();
else
dispatch_sync(dispatch_get_main_queue(), block);
return array;
}
- (void)restoreMemoryCache:(NSArray *)array
{
CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
for (NSString *url in array)
{
[self cachedImage:url availability:TGCacheDisk];
}
TGLog(@"Cache restored in %f ms", (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0);
}
@end