* master: (61 commits) Update Travis config for Xcode 10. Add test for object posting notification update README.md, the old "Foundation URL Loading System" link is 404 Change test name to correct one Change default teardown methods to tests Update tests to use new API Update PR feedback, add more tests Add test remove temporary method that could be doing nothing Update HTTPBin certificates. Chinese CONTRIBUTING.md translate Delete CONTRIBUTING_CH.md Chinese Contributing.md translate rollback method 'AFPostReachabilityStatusChange' Use AFNetworkReachabilityManager to compare consistency of notification posts and gets, instead of SCNetworkReachabilityRef. And test case reference. Remove test that is nilling session manually as it's not possible anymore to not have valid session (correctly) Add tests for verifing reachability manager consistence for notification posting and getting Specify Xcode 9.4 in Travis file. Update AFImageDownloader.m Add an ability that notification-observer know which reachability its belong ...
206 lines
7.6 KiB
Objective-C
206 lines
7.6 KiB
Objective-C
// 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
|