123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- //
- // YYImageCache.m
- // YYKit <https://github.com/ibireme/YYKit>
- //
- // Created by ibireme on 15/2/15.
- // Copyright (c) 2015 ibireme.
- //
- // This source code is licensed under the MIT-style license found in the
- // LICENSE file in the root directory of this source tree.
- //
- #import "YYImageCache.h"
- #import "YYMemoryCache.h"
- #import "YYDiskCache.h"
- #import "UIImage+YYAdd.h"
- #import "NSObject+YYAdd.h"
- #import "YYImage.h"
- #if __has_include("YYDispatchQueuePool.h")
- #import "YYDispatchQueuePool.h"
- #endif
- static inline dispatch_queue_t YYImageCacheIOQueue() {
- #ifdef YYDispatchQueuePool_h
- return YYDispatchQueueGetForQOS(NSQualityOfServiceDefault);
- #else
- return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- #endif
- }
- static inline dispatch_queue_t YYImageCacheDecodeQueue() {
- #ifdef YYDispatchQueuePool_h
- return YYDispatchQueueGetForQOS(NSQualityOfServiceUtility);
- #else
- return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
- #endif
- }
- @interface YYImageCache ()
- - (NSUInteger)imageCost:(UIImage *)image;
- - (UIImage *)imageFromData:(NSData *)data;
- @end
- @implementation YYImageCache
- - (NSUInteger)imageCost:(UIImage *)image {
- CGImageRef cgImage = image.CGImage;
- if (!cgImage) return 1;
- CGFloat height = CGImageGetHeight(cgImage);
- size_t bytesPerRow = CGImageGetBytesPerRow(cgImage);
- NSUInteger cost = bytesPerRow * height;
- if (cost == 0) cost = 1;
- return cost;
- }
- - (UIImage *)imageFromData:(NSData *)data {
- NSData *scaleData = [YYDiskCache getExtendedDataFromObject:data];
- CGFloat scale = 0;
- if (scaleData) {
- scale = ((NSNumber *)[NSKeyedUnarchiver unarchiveObjectWithData:scaleData]).doubleValue;
- }
- if (scale <= 0) scale = [UIScreen mainScreen].scale;
- UIImage *image;
- if (_allowAnimatedImage) {
- image = [[YYImage alloc] initWithData:data scale:scale];
- if (_decodeForDisplay) image = [image imageByDecoded];
- } else {
- YYImageDecoder *decoder = [YYImageDecoder decoderWithData:data scale:scale];
- image = [decoder frameAtIndex:0 decodeForDisplay:_decodeForDisplay].image;
- }
- return image;
- }
- #pragma mark Public
- + (instancetype)sharedCache {
- static YYImageCache *cache = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
- NSUserDomainMask, YES) firstObject];
- cachePath = [cachePath stringByAppendingPathComponent:@"com.ibireme.yykit"];
- cachePath = [cachePath stringByAppendingPathComponent:@"images"];
- cache = [[self alloc] initWithPath:cachePath];
- });
- return cache;
- }
- - (instancetype)init {
- @throw [NSException exceptionWithName:@"YYImageCache init error" reason:@"YYImageCache must be initialized with a path. Use 'initWithPath:' instead." userInfo:nil];
- return [self initWithPath:@""];
- }
- - (instancetype)initWithPath:(NSString *)path {
- YYMemoryCache *memoryCache = [YYMemoryCache new];
- memoryCache.shouldRemoveAllObjectsOnMemoryWarning = YES;
- memoryCache.shouldRemoveAllObjectsWhenEnteringBackground = YES;
- memoryCache.countLimit = NSUIntegerMax;
- memoryCache.costLimit = NSUIntegerMax;
- memoryCache.ageLimit = 12 * 60 * 60;
-
- YYDiskCache *diskCache = [[YYDiskCache alloc] initWithPath:path];
- diskCache.customArchiveBlock = ^(id object) { return (NSData *)object; };
- diskCache.customUnarchiveBlock = ^(NSData *data) { return (id)data; };
- if (!memoryCache || !diskCache) return nil;
-
- self = [super init];
- _memoryCache = memoryCache;
- _diskCache = diskCache;
- _allowAnimatedImage = YES;
- _decodeForDisplay = YES;
- return self;
- }
- - (void)setImage:(UIImage *)image forKey:(NSString *)key {
- [self setImage:image imageData:nil forKey:key withType:YYImageCacheTypeAll];
- }
- - (void)setImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSString *)key withType:(YYImageCacheType)type {
- if (!key || (image == nil && imageData.length == 0)) return;
-
- __weak typeof(self) _self = self;
- if (type & YYImageCacheTypeMemory) { // add to memory cache
- if (image) {
- if (image.isDecodedForDisplay) {
- [_memoryCache setObject:image forKey:key withCost:[_self imageCost:image]];
- } else {
- dispatch_async(YYImageCacheDecodeQueue(), ^{
- __strong typeof(_self) self = _self;
- if (!self) return;
- [self.memoryCache setObject:[image imageByDecoded] forKey:key withCost:[self imageCost:image]];
- });
- }
- } else if (imageData) {
- dispatch_async(YYImageCacheDecodeQueue(), ^{
- __strong typeof(_self) self = _self;
- if (!self) return;
- UIImage *newImage = [self imageFromData:imageData];
- [self.memoryCache setObject:newImage forKey:key withCost:[self imageCost:newImage]];
- });
- }
- }
- if (type & YYImageCacheTypeDisk) { // add to disk cache
- if (imageData) {
- if (image) {
- [YYDiskCache setExtendedData:[NSKeyedArchiver archivedDataWithRootObject:@(image.scale)] toObject:imageData];
- }
- [_diskCache setObject:imageData forKey:key];
- } else if (image) {
- dispatch_async(YYImageCacheIOQueue(), ^{
- __strong typeof(_self) self = _self;
- if (!self) return;
- NSData *data = [image imageDataRepresentation];
- [YYDiskCache setExtendedData:[NSKeyedArchiver archivedDataWithRootObject:@(image.scale)] toObject:data];
- [self.diskCache setObject:data forKey:key];
- });
- }
- }
- }
- - (void)removeImageForKey:(NSString *)key {
- [self removeImageForKey:key withType:YYImageCacheTypeAll];
- }
- - (void)removeImageForKey:(NSString *)key withType:(YYImageCacheType)type {
- if (type & YYImageCacheTypeMemory) [_memoryCache removeObjectForKey:key];
- if (type & YYImageCacheTypeDisk) [_diskCache removeObjectForKey:key];
- }
- - (BOOL)containsImageForKey:(NSString *)key {
- return [self containsImageForKey:key withType:YYImageCacheTypeAll];
- }
- - (BOOL)containsImageForKey:(NSString *)key withType:(YYImageCacheType)type {
- if (type & YYImageCacheTypeMemory) {
- if ([_memoryCache containsObjectForKey:key]) return YES;
- }
- if (type & YYImageCacheTypeDisk) {
- if ([_diskCache containsObjectForKey:key]) return YES;
- }
- return NO;
- }
- - (UIImage *)getImageForKey:(NSString *)key {
- return [self getImageForKey:key withType:YYImageCacheTypeAll];
- }
- - (UIImage *)getImageForKey:(NSString *)key withType:(YYImageCacheType)type {
- if (!key) return nil;
- if (type & YYImageCacheTypeMemory) {
- UIImage *image = [_memoryCache objectForKey:key];
- if (image) return image;
- }
- if (type & YYImageCacheTypeDisk) {
- NSData *data = (id)[_diskCache objectForKey:key];
- UIImage *image = [self imageFromData:data];
- if (image && (type & YYImageCacheTypeMemory)) {
- [_memoryCache setObject:image forKey:key withCost:[self imageCost:image]];
- }
- return image;
- }
- return nil;
- }
- - (void)getImageForKey:(NSString *)key withType:(YYImageCacheType)type withBlock:(void (^)(UIImage *image, YYImageCacheType type))block {
- if (!block) return;
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- UIImage *image = nil;
-
- if (type & YYImageCacheTypeMemory) {
- image = [_memoryCache objectForKey:key];
- if (image) {
- dispatch_async(dispatch_get_main_queue(), ^{
- block(image, YYImageCacheTypeMemory);
- });
- return;
- }
- }
-
- if (type & YYImageCacheTypeDisk) {
- NSData *data = (id)[_diskCache objectForKey:key];
- image = [self imageFromData:data];
- if (image) {
- [_memoryCache setObject:image forKey:key];
- dispatch_async(dispatch_get_main_queue(), ^{
- block(image, YYImageCacheTypeDisk);
- });
- return;
- }
- }
-
- dispatch_async(dispatch_get_main_queue(), ^{
- block(nil, YYImageCacheTypeNone);
- });
- });
- }
- - (NSData *)getImageDataForKey:(NSString *)key {
- return (id)[_diskCache objectForKey:key];
- }
- - (void)getImageDataForKey:(NSString *)key withBlock:(void (^)(NSData *imageData))block {
- if (!block) return;
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- NSData *data = (id)[_diskCache objectForKey:key];
- dispatch_async(dispatch_get_main_queue(), ^{
- block(data);
- });
- });
- }
- @end
|