You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
205 lines
7.6 KiB
205 lines
7.6 KiB
// AFAutoPurgingImageCache.m |
|
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ ) |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining a copy |
|
// of this software and associated documentation files (the "Software"), to deal |
|
// in the Software without restriction, including without limitation the rights |
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
// copies of the Software, and to permit persons to whom the Software is |
|
// furnished to do so, subject to the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be included in |
|
// all copies or substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
// THE SOFTWARE. |
|
|
|
#import <TargetConditionals.h> |
|
|
|
#if TARGET_OS_IOS || TARGET_OS_TV |
|
|
|
#import "AFAutoPurgingImageCache.h" |
|
|
|
@interface AFCachedImage : NSObject |
|
|
|
@property (nonatomic, strong) UIImage *image; |
|
@property (nonatomic, copy) NSString *identifier; |
|
@property (nonatomic, assign) UInt64 totalBytes; |
|
@property (nonatomic, strong) NSDate *lastAccessDate; |
|
@property (nonatomic, assign) UInt64 currentMemoryUsage; |
|
|
|
@end |
|
|
|
@implementation AFCachedImage |
|
|
|
- (instancetype)initWithImage:(UIImage *)image identifier:(NSString *)identifier { |
|
if (self = [self init]) { |
|
self.image = image; |
|
self.identifier = identifier; |
|
|
|
CGSize imageSize = CGSizeMake(image.size.width * image.scale, image.size.height * image.scale); |
|
CGFloat bytesPerPixel = 4.0; |
|
CGFloat bytesPerSize = imageSize.width * imageSize.height; |
|
self.totalBytes = (UInt64)bytesPerPixel * (UInt64)bytesPerSize; |
|
self.lastAccessDate = [NSDate date]; |
|
} |
|
return self; |
|
} |
|
|
|
- (UIImage *)accessImage { |
|
self.lastAccessDate = [NSDate date]; |
|
return self.image; |
|
} |
|
|
|
- (NSString *)description { |
|
NSString *descriptionString = [NSString stringWithFormat:@"Idenfitier: %@ lastAccessDate: %@ ", self.identifier, self.lastAccessDate]; |
|
return descriptionString; |
|
|
|
} |
|
|
|
@end |
|
|
|
@interface AFAutoPurgingImageCache () |
|
@property (nonatomic, strong) NSMutableDictionary <NSString* , AFCachedImage*> *cachedImages; |
|
@property (nonatomic, assign) UInt64 currentMemoryUsage; |
|
@property (nonatomic, strong) dispatch_queue_t synchronizationQueue; |
|
@end |
|
|
|
@implementation AFAutoPurgingImageCache |
|
|
|
- (instancetype)init { |
|
return [self initWithMemoryCapacity:100 * 1024 * 1024 preferredMemoryCapacity:60 * 1024 * 1024]; |
|
} |
|
|
|
- (instancetype)initWithMemoryCapacity:(UInt64)memoryCapacity preferredMemoryCapacity:(UInt64)preferredMemoryCapacity { |
|
if (self = [super init]) { |
|
self.memoryCapacity = memoryCapacity; |
|
self.preferredMemoryUsageAfterPurge = preferredMemoryCapacity; |
|
self.cachedImages = [[NSMutableDictionary alloc] init]; |
|
|
|
NSString *queueName = [NSString stringWithFormat:@"com.alamofire.autopurgingimagecache-%@", [[NSUUID UUID] UUIDString]]; |
|
self.synchronizationQueue = dispatch_queue_create([queueName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT); |
|
|
|
[[NSNotificationCenter defaultCenter] |
|
addObserver:self |
|
selector:@selector(removeAllImages) |
|
name:UIApplicationDidReceiveMemoryWarningNotification |
|
object:nil]; |
|
|
|
} |
|
return self; |
|
} |
|
|
|
- (void)dealloc { |
|
[[NSNotificationCenter defaultCenter] removeObserver:self]; |
|
} |
|
|
|
- (UInt64)memoryUsage { |
|
__block UInt64 result = 0; |
|
dispatch_sync(self.synchronizationQueue, ^{ |
|
result = self.currentMemoryUsage; |
|
}); |
|
return result; |
|
} |
|
|
|
- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier { |
|
dispatch_barrier_async(self.synchronizationQueue, ^{ |
|
AFCachedImage *cacheImage = [[AFCachedImage alloc] initWithImage:image identifier:identifier]; |
|
|
|
AFCachedImage *previousCachedImage = self.cachedImages[identifier]; |
|
if (previousCachedImage != nil) { |
|
self.currentMemoryUsage -= previousCachedImage.totalBytes; |
|
} |
|
|
|
self.cachedImages[identifier] = cacheImage; |
|
self.currentMemoryUsage += cacheImage.totalBytes; |
|
}); |
|
|
|
dispatch_barrier_async(self.synchronizationQueue, ^{ |
|
if (self.currentMemoryUsage > self.memoryCapacity) { |
|
UInt64 bytesToPurge = self.currentMemoryUsage - self.preferredMemoryUsageAfterPurge; |
|
NSMutableArray <AFCachedImage*> *sortedImages = [NSMutableArray arrayWithArray:self.cachedImages.allValues]; |
|
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastAccessDate" |
|
ascending:YES]; |
|
[sortedImages sortUsingDescriptors:@[sortDescriptor]]; |
|
|
|
UInt64 bytesPurged = 0; |
|
|
|
for (AFCachedImage *cachedImage in sortedImages) { |
|
[self.cachedImages removeObjectForKey:cachedImage.identifier]; |
|
bytesPurged += cachedImage.totalBytes; |
|
if (bytesPurged >= bytesToPurge) { |
|
break; |
|
} |
|
} |
|
self.currentMemoryUsage -= bytesPurged; |
|
} |
|
}); |
|
} |
|
|
|
- (BOOL)removeImageWithIdentifier:(NSString *)identifier { |
|
__block BOOL removed = NO; |
|
dispatch_barrier_sync(self.synchronizationQueue, ^{ |
|
AFCachedImage *cachedImage = self.cachedImages[identifier]; |
|
if (cachedImage != nil) { |
|
[self.cachedImages removeObjectForKey:identifier]; |
|
self.currentMemoryUsage -= cachedImage.totalBytes; |
|
removed = YES; |
|
} |
|
}); |
|
return removed; |
|
} |
|
|
|
- (BOOL)removeAllImages { |
|
__block BOOL removed = NO; |
|
dispatch_barrier_sync(self.synchronizationQueue, ^{ |
|
if (self.cachedImages.count > 0) { |
|
[self.cachedImages removeAllObjects]; |
|
self.currentMemoryUsage = 0; |
|
removed = YES; |
|
} |
|
}); |
|
return removed; |
|
} |
|
|
|
- (nullable UIImage *)imageWithIdentifier:(NSString *)identifier { |
|
__block UIImage *image = nil; |
|
dispatch_sync(self.synchronizationQueue, ^{ |
|
AFCachedImage *cachedImage = self.cachedImages[identifier]; |
|
image = [cachedImage accessImage]; |
|
}); |
|
return image; |
|
} |
|
|
|
- (void)addImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier { |
|
[self addImage:image withIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]]; |
|
} |
|
|
|
- (BOOL)removeImageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier { |
|
return [self removeImageWithIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]]; |
|
} |
|
|
|
- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier { |
|
return [self imageWithIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]]; |
|
} |
|
|
|
- (NSString *)imageCacheKeyFromURLRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)additionalIdentifier { |
|
NSString *key = request.URL.absoluteString; |
|
if (additionalIdentifier != nil) { |
|
key = [key stringByAppendingString:additionalIdentifier]; |
|
} |
|
return key; |
|
} |
|
|
|
- (BOOL)shouldCacheImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier { |
|
return YES; |
|
} |
|
|
|
@end |
|
|
|
#endif
|
|
|