Merge pull request #2968 from dreampiggy/feature_query_cache_type

Added new query cache type support, including the SDImageCache API and context option
This commit is contained in:
DreamPiggy 2020-04-02 16:55:56 +08:00 committed by GitHub
commit 51dee05acf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 388 additions and 27 deletions

View File

@ -235,6 +235,15 @@ typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) {
*/
- (nullable NSData *)diskImageDataForKey:(nullable NSString *)key;
/**
* Asynchronously load the image data in disk cache. You can decode the image data to image after loaded.
*
* @param key The unique key used to store the wanted image
* @param completionBlock the block to be executed when the check is done.
* @note the completion block will be always executed on the main queue
*/
- (void)diskImageDataQueryForKey:(nullable NSString *)key completion:(nullable SDImageCacheQueryDataCompletionBlock)completionBlock;
/**
* Operation that queries the cache asynchronously and call the completion when done.
*
@ -268,6 +277,19 @@ typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) {
*/
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options context:(nullable SDWebImageContext *)context done:(nullable SDImageCacheQueryCompletionBlock)doneBlock;
/**
* Asynchronously queries the cache with operation and call the completion when done.
*
* @param key The unique key used to store the wanted image. If you want transformed or thumbnail image, calculate the key with `SDTransformedKeyForKey`, `SDThumbnailedKeyForKey`, or generate the cache key from url with `cacheKeyForURL:context:`.
* @param options A mask to specify options to use for this cache query
* @param context A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold.
* @param queryCacheType Specify where to query the cache from. By default we use `.all`, which means both memory cache and disk cache. You can choose to query memory only or disk only as well. Pass `.none` is invalid and callback with nil immediatelly.
* @param doneBlock The completion block. Will not get called if the operation is cancelled
*
* @return a NSOperation instance containing the cache op
*/
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options context:(nullable SDWebImageContext *)context cacheType:(SDImageCacheType)queryCacheType done:(nullable SDImageCacheQueryCompletionBlock)doneBlock;
/**
* Synchronously query the memory cache.
*

View File

@ -314,6 +314,17 @@
return [self.diskCache containsDataForKey:key];
}
- (void)diskImageDataQueryForKey:(NSString *)key completion:(SDImageCacheQueryDataCompletionBlock)completionBlock {
dispatch_async(self.ioQueue, ^{
NSData *imageData = [self diskImageDataBySearchingAllPathsForKey:key];
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(imageData);
});
}
});
}
- (nullable NSData *)diskImageDataForKey:(nullable NSString *)key {
if (!key) {
return nil;
@ -426,12 +437,23 @@
}
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options context:(nullable SDWebImageContext *)context done:(nullable SDImageCacheQueryCompletionBlock)doneBlock {
return [self queryCacheOperationForKey:key options:options context:context cacheType:SDImageCacheTypeAll done:doneBlock];
}
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options context:(nullable SDWebImageContext *)context cacheType:(SDImageCacheType)queryCacheType done:(nullable SDImageCacheQueryCompletionBlock)doneBlock {
if (!key) {
if (doneBlock) {
doneBlock(nil, nil, SDImageCacheTypeNone);
}
return nil;
}
// Invalid cache type
if (queryCacheType == SDImageCacheTypeNone) {
if (doneBlock) {
doneBlock(nil, nil, SDImageCacheTypeNone);
}
return nil;
}
id<SDImageTransformer> transformer = context[SDWebImageContextImageTransformer];
if (transformer) {
@ -441,7 +463,10 @@
}
// First check the in-memory cache...
UIImage *image = [self imageFromMemoryCacheForKey:key];
UIImage *image;
if (queryCacheType != SDImageCacheTypeDisk) {
image = [self imageFromMemoryCacheForKey:key];
}
if (image) {
if (options & SDImageCacheDecodeFirstFrameOnly) {
@ -464,7 +489,7 @@
}
}
BOOL shouldQueryMemoryOnly = (image && !(options & SDImageCacheQueryMemoryData));
BOOL shouldQueryMemoryOnly = (queryCacheType == SDImageCacheTypeMemory) || (image && !(options & SDImageCacheQueryMemoryData));
if (shouldQueryMemoryOnly) {
if (doneBlock) {
doneBlock(image, nil, SDImageCacheTypeMemory);
@ -699,6 +724,10 @@
#pragma mark - SDImageCache
- (id<SDWebImageOperation>)queryImageForKey:(NSString *)key options:(SDWebImageOptions)options context:(nullable SDWebImageContext *)context completion:(nullable SDImageCacheQueryCompletionBlock)completionBlock {
return [self queryImageForKey:key options:options context:context cacheType:SDImageCacheTypeAll completion:completionBlock];
}
- (id<SDWebImageOperation>)queryImageForKey:(NSString *)key options:(SDWebImageOptions)options context:(nullable SDWebImageContext *)context cacheType:(SDImageCacheType)cacheType completion:(nullable SDImageCacheQueryCompletionBlock)completionBlock {
SDImageCacheOptions cacheOptions = 0;
if (options & SDWebImageQueryMemoryData) cacheOptions |= SDImageCacheQueryMemoryData;
if (options & SDWebImageQueryMemoryDataSync) cacheOptions |= SDImageCacheQueryMemoryDataSync;
@ -709,7 +738,7 @@
if (options & SDWebImagePreloadAllFrames) cacheOptions |= SDImageCachePreloadAllFrames;
if (options & SDWebImageMatchAnimatedImageClass) cacheOptions |= SDImageCacheMatchAnimatedImageClass;
return [self queryCacheOperationForKey:key options:cacheOptions context:context done:completionBlock];
return [self queryCacheOperationForKey:key options:cacheOptions context:context cacheType:cacheType done:completionBlock];
}
- (void)storeImage:(UIImage *)image imageData:(NSData *)imageData forKey:(nullable NSString *)key cacheType:(SDImageCacheType)cacheType completion:(nullable SDWebImageNoParamsBlock)completionBlock {

View File

@ -36,6 +36,7 @@ typedef NS_ENUM(NSInteger, SDImageCacheType) {
};
typedef void(^SDImageCacheCheckCompletionBlock)(BOOL isInCache);
typedef void(^SDImageCacheQueryDataCompletionBlock)(NSData * _Nullable data);
typedef void(^SDImageCacheCalculateSizeBlock)(NSUInteger fileCount, NSUInteger totalSize);
typedef NSString * _Nullable (^SDImageCacheAdditionalCachePathBlock)(NSString * _Nonnull key);
typedef void(^SDImageCacheQueryCompletionBlock)(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType);
@ -76,6 +77,23 @@ FOUNDATION_EXPORT UIImage * _Nullable SDImageCacheDecodeImageData(NSData * _Nonn
context:(nullable SDWebImageContext *)context
completion:(nullable SDImageCacheQueryCompletionBlock)completionBlock;
/**
Query the cached image from image cache for given key. The operation can be used to cancel the query.
If image is cached in memory, completion is called synchronously, else aynchronously and depends on the options arg (See `SDWebImageQueryDiskSync`)
@param key The image cache key
@param options A mask to specify options to use for this query
@param context A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold.
@param cacheType Specify where to query the cache from. By default we use `.all`, which means both memory cache and disk cache. You can choose to query memory only or disk only as well. Pass `.none` is invalid and callback with nil immediatelly.
@param completionBlock The completion block. Will not get called if the operation is cancelled
@return The operation for this query
*/
- (nullable id<SDWebImageOperation>)queryImageForKey:(nullable NSString *)key
options:(SDWebImageOptions)options
context:(nullable SDWebImageContext *)context
cacheType:(SDImageCacheType)cacheType
completion:(nullable SDImageCacheQueryCompletionBlock)completionBlock;
/**
Store the image into image cache for the given key. If cache type is memory only, completion is called synchronously, else aynchronously.

View File

@ -85,6 +85,10 @@
#pragma mark - SDImageCache
- (id<SDWebImageOperation>)queryImageForKey:(NSString *)key options:(SDWebImageOptions)options context:(SDWebImageContext *)context completion:(SDImageCacheQueryCompletionBlock)completionBlock {
return [self queryImageForKey:key options:options context:context cacheType:SDImageCacheTypeAll completion:completionBlock];
}
- (id<SDWebImageOperation>)queryImageForKey:(NSString *)key options:(SDWebImageOptions)options context:(SDWebImageContext *)context cacheType:(SDImageCacheType)cacheType completion:(SDImageCacheQueryCompletionBlock)completionBlock {
if (!key) {
return nil;
}
@ -93,30 +97,30 @@
if (count == 0) {
return nil;
} else if (count == 1) {
return [caches.firstObject queryImageForKey:key options:options context:context completion:completionBlock];
return [caches.firstObject queryImageForKey:key options:options context:context cacheType:cacheType completion:completionBlock];
}
switch (self.queryOperationPolicy) {
case SDImageCachesManagerOperationPolicyHighestOnly: {
id<SDImageCache> cache = caches.lastObject;
return [cache queryImageForKey:key options:options context:context completion:completionBlock];
return [cache queryImageForKey:key options:options context:context cacheType:cacheType completion:completionBlock];
}
break;
case SDImageCachesManagerOperationPolicyLowestOnly: {
id<SDImageCache> cache = caches.firstObject;
return [cache queryImageForKey:key options:options context:context completion:completionBlock];
return [cache queryImageForKey:key options:options context:context cacheType:cacheType completion:completionBlock];
}
break;
case SDImageCachesManagerOperationPolicyConcurrent: {
SDImageCachesManagerOperation *operation = [SDImageCachesManagerOperation new];
[operation beginWithTotalCount:caches.count];
[self concurrentQueryImageForKey:key options:options context:context completion:completionBlock enumerator:caches.reverseObjectEnumerator operation:operation];
[self concurrentQueryImageForKey:key options:options context:context cacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator operation:operation];
return operation;
}
break;
case SDImageCachesManagerOperationPolicySerial: {
SDImageCachesManagerOperation *operation = [SDImageCachesManagerOperation new];
[operation beginWithTotalCount:caches.count];
[self serialQueryImageForKey:key options:options context:context completion:completionBlock enumerator:caches.reverseObjectEnumerator operation:operation];
[self serialQueryImageForKey:key options:options context:context cacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator operation:operation];
return operation;
}
break;
@ -279,11 +283,11 @@
#pragma mark - Concurrent Operation
- (void)concurrentQueryImageForKey:(NSString *)key options:(SDWebImageOptions)options context:(SDWebImageContext *)context completion:(SDImageCacheQueryCompletionBlock)completionBlock enumerator:(NSEnumerator<id<SDImageCache>> *)enumerator operation:(SDImageCachesManagerOperation *)operation {
- (void)concurrentQueryImageForKey:(NSString *)key options:(SDWebImageOptions)options context:(SDWebImageContext *)context cacheType:(SDImageCacheType)queryCacheType completion:(SDImageCacheQueryCompletionBlock)completionBlock enumerator:(NSEnumerator<id<SDImageCache>> *)enumerator operation:(SDImageCachesManagerOperation *)operation {
NSParameterAssert(enumerator);
NSParameterAssert(operation);
for (id<SDImageCache> cache in enumerator) {
[cache queryImageForKey:key options:options context:context completion:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) {
[cache queryImageForKey:key options:options context:context cacheType:queryCacheType completion:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) {
if (operation.isCancelled) {
// Cancelled
return;
@ -422,7 +426,7 @@
#pragma mark - Serial Operation
- (void)serialQueryImageForKey:(NSString *)key options:(SDWebImageOptions)options context:(SDWebImageContext *)context completion:(SDImageCacheQueryCompletionBlock)completionBlock enumerator:(NSEnumerator<id<SDImageCache>> *)enumerator operation:(SDImageCachesManagerOperation *)operation {
- (void)serialQueryImageForKey:(NSString *)key options:(SDWebImageOptions)options context:(SDWebImageContext *)context cacheType:(SDImageCacheType)queryCacheType completion:(SDImageCacheQueryCompletionBlock)completionBlock enumerator:(NSEnumerator<id<SDImageCache>> *)enumerator operation:(SDImageCachesManagerOperation *)operation {
NSParameterAssert(enumerator);
NSParameterAssert(operation);
id<SDImageCache> cache = enumerator.nextObject;
@ -435,7 +439,7 @@
return;
}
@weakify(self);
[cache queryImageForKey:key options:options context:context completion:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) {
[cache queryImageForKey:key options:options context:context cacheType:queryCacheType completion:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) {
@strongify(self);
if (operation.isCancelled) {
// Cancelled
@ -455,7 +459,7 @@
return;
}
// Next
[self serialQueryImageForKey:key options:options context:context completion:completionBlock enumerator:enumerator operation:operation];
[self serialQueryImageForKey:key options:options context:context cacheType:queryCacheType completion:completionBlock enumerator:enumerator operation:operation];
}];
}

View File

@ -259,6 +259,12 @@ FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextImageP
*/
FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextImageThumbnailPixelSize;
/**
A SDImageCacheType raw value which specify the source of cache to query. Specify `SDImageCacheTypeDisk` to query from disk cache only; `SDImageCacheTypeMemory` to query from memory only. And `SDImageCacheTypeAll` to query from both memory cache and disk cache. Specify `SDImageCacheTypeNone` is invalid and totally ignore the cache query.
If not provide or the value is invalid, we will use `SDImageCacheTypeAll`. (NSNumber)
*/
FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextQueryCacheType;
/**
A SDImageCacheType raw value which specify the store cache type when the image has just been downloaded and will be stored to the cache. Specify `SDImageCacheTypeNone` to disable cache storage; `SDImageCacheTypeDisk` to store in disk cache only; `SDImageCacheTypeMemory` to store in memory only. And `SDImageCacheTypeAll` to store in both memory cache and disk cache.
If you use image transformer feature, this actually apply for the transformed image, but not the original image itself. Use `SDWebImageContextOriginalStoreCacheType` if you want to control the original image's store cache type at the same time.

View File

@ -127,6 +127,7 @@ SDWebImageContextOption const SDWebImageContextImageTransformer = @"imageTransfo
SDWebImageContextOption const SDWebImageContextImageScaleFactor = @"imageScaleFactor";
SDWebImageContextOption const SDWebImageContextImagePreserveAspectRatio = @"imagePreserveAspectRatio";
SDWebImageContextOption const SDWebImageContextImageThumbnailPixelSize = @"imageThumbnailPixelSize";
SDWebImageContextOption const SDWebImageContextQueryCacheType = @"queryCacheType";
SDWebImageContextOption const SDWebImageContextStoreCacheType = @"storeCacheType";
SDWebImageContextOption const SDWebImageContextOriginalStoreCacheType = @"originalStoreCacheType";
SDWebImageContextOption const SDWebImageContextAnimatedImageClass = @"animatedImageClass";

View File

@ -220,10 +220,15 @@ static id<SDImageLoader> _defaultImageLoader;
}
// Check whether we should query cache
BOOL shouldQueryCache = !SD_OPTIONS_CONTAINS(options, SDWebImageFromLoaderOnly);
// Get the query cache type
SDImageCacheType queryCacheType = SDImageCacheTypeAll;
if (context[SDWebImageContextQueryCacheType]) {
queryCacheType = [context[SDWebImageContextQueryCacheType] integerValue];
}
if (shouldQueryCache) {
NSString *key = [self cacheKeyForURL:url context:context];
@weakify(operation);
operation.cacheOperation = [imageCache queryImageForKey:key options:options context:context completion:^(UIImage * _Nullable cachedImage, NSData * _Nullable cachedData, SDImageCacheType cacheType) {
operation.cacheOperation = [imageCache queryImageForKey:key options:options context:context cacheType:queryCacheType completion:^(UIImage * _Nullable cachedImage, NSData * _Nullable cachedData, SDImageCacheType cacheType) {
@strongify(operation);
if (!operation || operation.isCancelled) {
// Image combined operation cancelled by user

View File

@ -610,7 +610,7 @@ static NSString *kTestImageKeyPNG = @"TestImageKey.png";
- (void)test50SDImageCacheQueryOp {
XCTestExpectation *expectation = [self expectationWithDescription:@"SDImageCache query op works"];
[[SDImageCache sharedImageCache] storeImage:[self testJPEGImage] forKey:kTestImageKeyJPEG toDisk:NO completion:nil];
[[SDImageCachesManager sharedManager] queryImageForKey:kTestImageKeyJPEG options:0 context:nil completion:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) {
[[SDImageCachesManager sharedManager] queryImageForKey:kTestImageKeyJPEG options:0 context:nil cacheType:SDImageCacheTypeAll completion:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) {
expect(image).notTo.beNil();
[expectation fulfill];
}];
@ -680,7 +680,7 @@ static NSString *kTestImageKeyPNG = @"TestImageKey.png";
cachesManager.removeOperationPolicy = SDImageCachesManagerOperationPolicyLowestOnly;
cachesManager.containsOperationPolicy = SDImageCachesManagerOperationPolicyLowestOnly;
cachesManager.clearOperationPolicy = SDImageCachesManagerOperationPolicyLowestOnly;
[cachesManager queryImageForKey:kTestImageKeyJPEG options:0 context:nil completion:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) {
[cachesManager queryImageForKey:kTestImageKeyJPEG options:0 context:nil cacheType:SDImageCacheTypeAll completion:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) {
expect(image).to.beNil();
}];
[cachesManager storeImage:[self testJPEGImage] imageData:nil forKey:kTestImageKeyJPEG cacheType:SDImageCacheTypeMemory completion:nil];
@ -699,7 +699,7 @@ static NSString *kTestImageKeyPNG = @"TestImageKey.png";
cachesManager.removeOperationPolicy = SDImageCachesManagerOperationPolicyHighestOnly;
cachesManager.containsOperationPolicy = SDImageCachesManagerOperationPolicyHighestOnly;
cachesManager.clearOperationPolicy = SDImageCachesManagerOperationPolicyHighestOnly;
[cachesManager queryImageForKey:kTestImageKeyPNG options:0 context:nil completion:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) {
[cachesManager queryImageForKey:kTestImageKeyPNG options:0 context:nil cacheType:SDImageCacheTypeAll completion:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) {
expect(image).to.beNil();
}];
[cachesManager storeImage:[self testPNGImage] imageData:nil forKey:kTestImageKeyPNG cacheType:SDImageCacheTypeMemory completion:nil];
@ -732,7 +732,7 @@ static NSString *kTestImageKeyPNG = @"TestImageKey.png";
cachesManager.removeOperationPolicy = SDImageCachesManagerOperationPolicyConcurrent;
cachesManager.containsOperationPolicy = SDImageCachesManagerOperationPolicyConcurrent;
cachesManager.clearOperationPolicy = SDImageCachesManagerOperationPolicyConcurrent;
[cachesManager queryImageForKey:kConcurrentTestImageKey options:0 context:nil completion:nil];
[cachesManager queryImageForKey:kConcurrentTestImageKey options:0 context:nil cacheType:SDImageCacheTypeAll completion:nil];
[cachesManager storeImage:[self testJPEGImage] imageData:nil forKey:kConcurrentTestImageKey cacheType:SDImageCacheTypeMemory completion:nil];
[cachesManager removeImageForKey:kConcurrentTestImageKey cacheType:SDImageCacheTypeMemory completion:nil];
[cachesManager clearWithCacheType:SDImageCacheTypeMemory completion:nil];
@ -772,7 +772,7 @@ static NSString *kTestImageKeyPNG = @"TestImageKey.png";
cachesManager.removeOperationPolicy = SDImageCachesManagerOperationPolicySerial;
cachesManager.containsOperationPolicy = SDImageCachesManagerOperationPolicySerial;
cachesManager.clearOperationPolicy = SDImageCachesManagerOperationPolicySerial;
[cachesManager queryImageForKey:kSerialTestImageKey options:0 context:nil completion:nil];
[cachesManager queryImageForKey:kSerialTestImageKey options:0 context:nil cacheType:SDImageCacheTypeAll completion:nil];
[cachesManager storeImage:[self testJPEGImage] imageData:nil forKey:kSerialTestImageKey cacheType:SDImageCacheTypeMemory completion:nil];
[cachesManager removeImageForKey:kSerialTestImageKey cacheType:SDImageCacheTypeMemory completion:nil];
[cachesManager clearWithCacheType:SDImageCacheTypeMemory completion:nil];
@ -793,6 +793,41 @@ static NSString *kTestImageKeyPNG = @"TestImageKey.png";
[self waitForExpectationsWithCommonTimeout];
}
- (void)test58CustomImageCache {
NSString *cachePath = [[self userCacheDirectory] stringByAppendingPathComponent:@"custom"];
SDImageCacheConfig *config = [[SDImageCacheConfig alloc] init];
SDWebImageTestCache *cache = [[SDWebImageTestCache alloc] initWithCachePath:cachePath config:config];
expect(cache.memoryCache).notTo.beNil();
expect(cache.diskCache).notTo.beNil();
// Clear
[cache clearWithCacheType:SDImageCacheTypeAll completion:nil];
// Store
UIImage *image1 = self.testJPEGImage;
NSString *key1 = @"testJPEGImage";
[cache storeImage:image1 imageData:nil forKey:key1 cacheType:SDImageCacheTypeAll completion:nil];
// Contain
[cache containsImageForKey:key1 cacheType:SDImageCacheTypeAll completion:^(SDImageCacheType containsCacheType) {
expect(containsCacheType).equal(SDImageCacheTypeMemory);
}];
// Query
[cache queryImageForKey:key1 options:0 context:nil cacheType:SDImageCacheTypeAll completion:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) {
expect(image).equal(image1);
expect(data).beNil();
expect(cacheType).equal(SDImageCacheTypeMemory);
}];
// Remove
[cache removeImageForKey:key1 cacheType:SDImageCacheTypeAll completion:nil];
// Contain
[cache containsImageForKey:key1 cacheType:SDImageCacheTypeAll completion:^(SDImageCacheType containsCacheType) {
expect(containsCacheType).equal(SDImageCacheTypeNone);
}];
// Clear
[cache clearWithCacheType:SDImageCacheTypeAll completion:nil];
NSArray<NSString *> *cacheFiles = [cache.diskCache.fileManager contentsOfDirectoryAtPath:cachePath error:nil];
expect(cacheFiles.count).equal(0);
}
#pragma mark Helper methods
- (UIImage *)testJPEGImage {

View File

@ -474,7 +474,6 @@
[self waitForExpectationsWithCommonTimeout];
}
#if SD_UIKIT
- (void)test22ThatCustomDecoderWorksForImageDownload {
XCTestExpectation *expectation = [self expectationWithDescription:@"Custom decoder for SDWebImageDownloader not works"];
SDWebImageDownloader *downloader = [[SDWebImageDownloader alloc] init];
@ -487,8 +486,8 @@
UIImage *testJPEGImage = [[UIImage alloc] initWithContentsOfFile:testJPEGImagePath];
[downloader downloadImageWithURL:testImageURL options:0 progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
NSData *data1 = UIImagePNGRepresentation(testJPEGImage);
NSData *data2 = UIImagePNGRepresentation(image);
NSData *data1 = [testJPEGImage sd_imageDataAsFormat:SDImageFormatPNG];
NSData *data2 = [image sd_imageDataAsFormat:SDImageFormatPNG];
if (![data1 isEqualToData:data2]) {
XCTFail(@"The image data is not equal to cutom decoder, check -[SDWebImageTestDecoder decodedImageWithData:]");
}
@ -499,7 +498,6 @@
[self waitForExpectationsWithCommonTimeout];
[downloader invalidateSessionAndCancel:YES];
}
#endif
- (void)test23ThatDownloadRequestModifierWorks {
XCTestExpectation *expectation = [self expectationWithDescription:@"Download request modifier not works"];

View File

@ -8,6 +8,8 @@
#import "SDTestCase.h"
#import "SDWebImageTestTransformer.h"
#import "SDWebImageTestCache.h"
#import "SDWebImageTestLoader.h"
@interface SDWebImageManagerTests : SDTestCase
@ -268,6 +270,53 @@
}];
}
- (void)test14ThatCustomCacheAndLoaderWorks {
XCTestExpectation *expectation = [self expectationWithDescription:@"Custom Cache and Loader during manger query"];
NSURL *url = [NSURL URLWithString:@"http://via.placeholder.com/100x100.png"];
SDWebImageContext *context = @{
SDWebImageContextImageCache : SDWebImageTestCache.sharedCache,
SDWebImageContextImageLoader : SDWebImageTestLoader.sharedLoader
};
[SDWebImageTestCache.sharedCache clearWithCacheType:SDImageCacheTypeAll completion:nil];
[SDWebImageManager.sharedManager loadImageWithURL:url options:SDWebImageWaitStoreCache context:context progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
expect(image).notTo.beNil();
expect(image.size.width).equal(100);
expect(image.size.height).equal(100);
expect(data).notTo.beNil();
NSString *cacheKey = [SDWebImageManager.sharedManager cacheKeyForURL:imageURL];
// Check Disk Cache (SDWebImageWaitStoreCache behavior)
[SDWebImageTestCache.sharedCache containsImageForKey:cacheKey cacheType:SDImageCacheTypeDisk completion:^(SDImageCacheType containsCacheType) {
expect(containsCacheType).equal(SDImageCacheTypeDisk);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithCommonTimeout];
}
- (void)test15ThatQueryCacheTypeWork {
XCTestExpectation *expectation = [self expectationWithDescription:@"Image query cache type works"];
NSURL *url = [NSURL URLWithString:@"http://via.placeholder.com/101x101.png"];
NSString *key = [SDWebImageManager.sharedManager cacheKeyForURL:url];
NSData *testImageData = [NSData dataWithContentsOfFile:[self testJPEGPath]];
[SDImageCache.sharedImageCache storeImageDataToDisk:testImageData forKey:key];
// Query memory first
[SDWebImageManager.sharedManager loadImageWithURL:url options:SDWebImageFromCacheOnly context:@{SDWebImageContextQueryCacheType : @(SDImageCacheTypeMemory)} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
expect(image).beNil();
expect(cacheType).equal(SDImageCacheTypeNone);
// Query disk secondly
[SDWebImageManager.sharedManager loadImageWithURL:url options:SDWebImageFromCacheOnly context:@{SDWebImageContextQueryCacheType : @(SDImageCacheTypeDisk)} progress:nil completed:^(UIImage * _Nullable image2, NSData * _Nullable data2, NSError * _Nullable error2, SDImageCacheType cacheType2, BOOL finished2, NSURL * _Nullable imageURL2) {
expect(image2).notTo.beNil();
expect(cacheType2).equal(SDImageCacheTypeDisk);
[SDImageCache.sharedImageCache removeImageFromDiskForKey:key];
[expectation fulfill];
}];
}];
[self waitForExpectationsWithCommonTimeout];
}
- (NSString *)testJPEGPath {
NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
return [testBundle pathForResource:@"TestImage" ofType:@"jpg"];

View File

@ -9,9 +9,9 @@
#import <SDWebImage/SDMemoryCache.h>
#import <SDWebImage/SDDiskCache.h>
#import <SDWebImage/SDImageCacheDefine.h>
// A really naive implementation of custom memory cache and disk cache
@interface SDWebImageTestMemoryCache : NSObject <SDMemoryCache>
@property (nonatomic, strong, nonnull) SDImageCacheConfig *config;
@ -26,3 +26,16 @@
@property (nonatomic, strong, nonnull) NSFileManager *fileManager;
@end
// A really naive implementation of custom image cache using memory cache and disk cache
@interface SDWebImageTestCache : NSObject <SDImageCache>
@property (nonatomic, strong, nonnull) SDImageCacheConfig *config;
@property (nonatomic, strong, nonnull) SDWebImageTestMemoryCache *memoryCache;
@property (nonatomic, strong, nonnull) SDWebImageTestDiskCache *diskCache;
- (nullable instancetype)initWithCachePath:(nonnull NSString *)cachePath config:(nonnull SDImageCacheConfig *)config;
@property (nonatomic, class, readonly, nonnull) SDWebImageTestCache *sharedCache;
@end

View File

@ -8,7 +8,7 @@
*/
#import "SDWebImageTestCache.h"
#import <SDWebImage/SDImageCacheConfig.h>
#import <SDWebImage/SDWebImage.h>
#import "SDFileAttributeHelper.h"
static NSString * const SDWebImageTestDiskCacheExtendedAttributeName = @"com.hackemist.SDWebImageTestDiskCache";
@ -49,7 +49,7 @@ static NSString * const SDWebImageTestDiskCacheExtendedAttributeName = @"com.hac
@implementation SDWebImageTestDiskCache
- (nullable NSString *)cachePathForKey:(nonnull NSString *)key {
return [self.cachePath stringByAppendingPathComponent:key];
return [self.cachePath stringByAppendingPathComponent:key.lastPathComponent];
}
- (BOOL)containsDataForKey:(nonnull NSString *)key {
@ -72,7 +72,10 @@ static NSString * const SDWebImageTestDiskCacheExtendedAttributeName = @"com.hac
}
- (void)removeAllData {
[self.fileManager removeItemAtPath:self.cachePath error:nil];
for (NSString *path in [self.fileManager subpathsAtPath:self.cachePath]) {
NSString *filePath = [self.cachePath stringByAppendingPathComponent:path];
[self.fileManager removeItemAtPath:filePath error:nil];
}
}
- (void)removeDataForKey:(nonnull NSString *)key {
@ -122,3 +125,170 @@ static NSString * const SDWebImageTestDiskCacheExtendedAttributeName = @"com.hac
}
@end
@implementation SDWebImageTestCache
+ (SDWebImageTestCache *)sharedCache {
static dispatch_once_t onceToken;
static SDWebImageTestCache *cache;
dispatch_once(&onceToken, ^{
NSString *cachePath = [[self userCacheDirectory] stringByAppendingPathComponent:@"SDWebImageTestCache"];
SDImageCacheConfig *config = SDImageCacheConfig.defaultCacheConfig;
cache = [[SDWebImageTestCache alloc] initWithCachePath:cachePath config:config];
});
return cache;
}
- (instancetype)initWithCachePath:(NSString *)cachePath config:(SDImageCacheConfig *)config {
self = [super init];
if (self) {
self.config = config;
self.memoryCache = [[SDWebImageTestMemoryCache alloc] initWithConfig:config];
self.diskCache = [[SDWebImageTestDiskCache alloc] initWithCachePath:cachePath config:config];
}
return self;
}
- (void)clearWithCacheType:(SDImageCacheType)cacheType completion:(nullable SDWebImageNoParamsBlock)completionBlock {
switch (cacheType) {
case SDImageCacheTypeNone:
break;
case SDImageCacheTypeMemory:
[self.memoryCache removeAllObjects];
break;
case SDImageCacheTypeDisk:
[self.diskCache removeAllData];
break;
case SDImageCacheTypeAll:
[self.memoryCache removeAllObjects];
[self.diskCache removeAllData];
break;
default:
break;
}
if (completionBlock) {
completionBlock();
}
}
- (void)containsImageForKey:(nullable NSString *)key cacheType:(SDImageCacheType)cacheType completion:(nullable SDImageCacheContainsCompletionBlock)completionBlock {
SDImageCacheType containsCacheType = SDImageCacheTypeNone;
switch (cacheType) {
case SDImageCacheTypeNone:
break;
case SDImageCacheTypeMemory:
containsCacheType = [self.memoryCache objectForKey:key] ? SDImageCacheTypeMemory : SDImageCacheTypeNone;
break;
case SDImageCacheTypeDisk:
containsCacheType = [self.diskCache containsDataForKey:key] ? SDImageCacheTypeDisk : SDImageCacheTypeNone;
break;
case SDImageCacheTypeAll:
if ([self.memoryCache objectForKey:key]) {
containsCacheType = SDImageCacheTypeMemory;
} else if ([self.diskCache containsDataForKey:key]) {
containsCacheType = SDImageCacheTypeDisk;
}
break;
default:
break;
}
if (completionBlock) {
completionBlock(containsCacheType);
}
}
- (nullable id<SDWebImageOperation>)queryImageForKey:(nullable NSString *)key options:(SDWebImageOptions)options context:(nullable SDWebImageContext *)context completion:(nullable SDImageCacheQueryCompletionBlock)completionBlock {
return [self queryImageForKey:key options:options context:context cacheType:SDImageCacheTypeAll completion:completionBlock];
}
- (nullable id<SDWebImageOperation>)queryImageForKey:(nullable NSString *)key options:(SDWebImageOptions)options context:(nullable SDWebImageContext *)context cacheType:(SDImageCacheType)cacheType completion:(nullable SDImageCacheQueryCompletionBlock)completionBlock {
UIImage *image;
NSData *data;
SDImageCacheType resultCacheType = SDImageCacheTypeNone;
switch (cacheType) {
case SDImageCacheTypeNone:
break;
case SDImageCacheTypeMemory:
image = [self.memoryCache objectForKey:key];
if (image) {
resultCacheType = SDImageCacheTypeMemory;
}
break;
case SDImageCacheTypeDisk:
data = [self.diskCache dataForKey:key];
image = [UIImage sd_imageWithData:data];
if (data) {
resultCacheType = SDImageCacheTypeDisk;
}
break;
case SDImageCacheTypeAll:
image = [self.memoryCache objectForKey:key];
if (image) {
resultCacheType = SDImageCacheTypeMemory;
} else {
data = [self.diskCache dataForKey:key];
image = [UIImage sd_imageWithData:data];
if (data) {
resultCacheType = SDImageCacheTypeDisk;
}
}
break;
default:
break;
}
if (completionBlock) {
completionBlock(image, data, resultCacheType);
}
return nil;
}
- (void)removeImageForKey:(nullable NSString *)key cacheType:(SDImageCacheType)cacheType completion:(nullable SDWebImageNoParamsBlock)completionBlock {
switch (cacheType) {
case SDImageCacheTypeNone:
break;
case SDImageCacheTypeMemory:
[self.memoryCache removeObjectForKey:key];
break;
case SDImageCacheTypeDisk:
[self.diskCache removeDataForKey:key];
break;
case SDImageCacheTypeAll:
[self.memoryCache removeObjectForKey:key];
[self.diskCache removeDataForKey:key];
break;
default:
break;
}
if (completionBlock) {
completionBlock();
}
}
- (void)storeImage:(nullable UIImage *)image imageData:(nullable NSData *)imageData forKey:(nullable NSString *)key cacheType:(SDImageCacheType)cacheType completion:(nullable SDWebImageNoParamsBlock)completionBlock {
switch (cacheType) {
case SDImageCacheTypeNone:
break;
case SDImageCacheTypeMemory:
[self.memoryCache setObject:image forKey:key];
break;
case SDImageCacheTypeDisk:
[self.diskCache setData:imageData forKey:key];
break;
case SDImageCacheTypeAll:
[self.memoryCache setObject:image forKey:key];
[self.diskCache setData:imageData forKey:key];
break;
default:
break;
}
if (completionBlock) {
completionBlock();
}
}
+ (nullable NSString *)userCacheDirectory {
NSArray<NSString *> *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
return paths.firstObject;
}
@end

View File

@ -13,4 +13,6 @@
// A really naive implementation of custom image loader using `NSURLSession`
@interface SDWebImageTestLoader : NSObject <SDImageLoader>
@property (nonatomic, class, readonly, nonnull) SDWebImageTestLoader *sharedLoader;
@end

View File

@ -16,6 +16,15 @@
@implementation SDWebImageTestLoader
+ (SDWebImageTestLoader *)sharedLoader {
static dispatch_once_t onceToken;
static SDWebImageTestLoader *loader;
dispatch_once(&onceToken, ^{
loader = [[SDWebImageTestLoader alloc] init];
});
return loader;
}
- (BOOL)canRequestImageForURL:(NSURL *)url {
return YES;
}