Merge pull request #2277 from dreampiggy/refactor_cache_revert_handle_disk_error

Revert "Merge pull request #1898 from walkline/master into 5.x"
This commit is contained in:
DreamPiggy 2018-04-13 17:32:29 +08:00 committed by GitHub
commit 95ace107e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 43 additions and 72 deletions

View File

@ -56,8 +56,6 @@ typedef void(^SDWebImageCheckCacheCompletionBlock)(BOOL isInCache);
typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger totalSize); typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger totalSize);
typedef void(^SDWebImageCompletionWithPossibleErrorBlock)(NSError * _Nullable error);
typedef NSString * _Nullable (^SDImageCacheAdditionalCachePathBlock)(NSString * _Nonnull key); typedef NSString * _Nullable (^SDImageCacheAdditionalCachePathBlock)(NSString * _Nonnull key);
/** /**
@ -140,7 +138,7 @@ typedef NSString * _Nullable (^SDImageCacheAdditionalCachePathBlock)(NSString *
*/ */
- (void)storeImage:(nullable UIImage *)image - (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key forKey:(nullable NSString *)key
completion:(nullable SDWebImageCompletionWithPossibleErrorBlock)completionBlock; completion:(nullable SDWebImageNoParamsBlock)completionBlock;
/** /**
* Asynchronously store an image into memory and disk cache at the given key. * Asynchronously store an image into memory and disk cache at the given key.
@ -153,7 +151,7 @@ typedef NSString * _Nullable (^SDImageCacheAdditionalCachePathBlock)(NSString *
- (void)storeImage:(nullable UIImage *)image - (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk toDisk:(BOOL)toDisk
completion:(nullable SDWebImageCompletionWithPossibleErrorBlock)completionBlock; completion:(nullable SDWebImageNoParamsBlock)completionBlock;
/** /**
* Asynchronously store an image into memory and disk cache at the given key. * Asynchronously store an image into memory and disk cache at the given key.
@ -170,18 +168,16 @@ typedef NSString * _Nullable (^SDImageCacheAdditionalCachePathBlock)(NSString *
imageData:(nullable NSData *)imageData imageData:(nullable NSData *)imageData
forKey:(nullable NSString *)key forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk toDisk:(BOOL)toDisk
completion:(nullable SDWebImageCompletionWithPossibleErrorBlock)completionBlock; completion:(nullable SDWebImageNoParamsBlock)completionBlock;
/** /**
* Synchronously store image NSData into disk cache at the given key. * Synchronously store image NSData into disk cache at the given key.
* *
* @param imageData The image data to store * @param imageData The image data to store
* @param key The unique image cache key, usually it's image absolute URL * @param key The unique image cache key, usually it's image absolute URL
* @param error NSError pointer, for possible file I/O errors, See FoundationErrors.h
*/ */
- (BOOL)storeImageDataToDisk:(nullable NSData *)imageData - (void)storeImageDataToDisk:(nullable NSData *)imageData
forKey:(nullable NSString *)key forKey:(nullable NSString *)key;
error:(NSError * _Nullable * _Nullable)error;
#pragma mark - Query and Retrieve Ops #pragma mark - Query and Retrieve Ops

View File

@ -252,14 +252,14 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
- (void)storeImage:(nullable UIImage *)image - (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key forKey:(nullable NSString *)key
completion:(nullable SDWebImageCompletionWithPossibleErrorBlock)completionBlock { completion:(nullable SDWebImageNoParamsBlock)completionBlock {
[self storeImage:image imageData:nil forKey:key toDisk:YES completion:completionBlock]; [self storeImage:image imageData:nil forKey:key toDisk:YES completion:completionBlock];
} }
- (void)storeImage:(nullable UIImage *)image - (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk toDisk:(BOOL)toDisk
completion:(nullable SDWebImageCompletionWithPossibleErrorBlock)completionBlock { completion:(nullable SDWebImageNoParamsBlock)completionBlock {
[self storeImage:image imageData:nil forKey:key toDisk:toDisk completion:completionBlock]; [self storeImage:image imageData:nil forKey:key toDisk:toDisk completion:completionBlock];
} }
@ -267,10 +267,10 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
imageData:(nullable NSData *)imageData imageData:(nullable NSData *)imageData
forKey:(nullable NSString *)key forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk toDisk:(BOOL)toDisk
completion:(nullable SDWebImageCompletionWithPossibleErrorBlock)completionBlock { completion:(nullable SDWebImageNoParamsBlock)completionBlock {
if (!image || !key) { if (!image || !key) {
if (completionBlock) { if (completionBlock) {
completionBlock(nil); completionBlock();
} }
return; return;
} }
@ -282,7 +282,6 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
if (toDisk) { if (toDisk) {
dispatch_async(self.ioQueue, ^{ dispatch_async(self.ioQueue, ^{
NSError * writeError = nil;
@autoreleasepool { @autoreleasepool {
NSData *data = imageData; NSData *data = imageData;
if (!data && image) { if (!data && image) {
@ -295,50 +294,41 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
} }
data = [[SDWebImageCodersManager sharedManager] encodedDataWithImage:image format:format options:nil]; data = [[SDWebImageCodersManager sharedManager] encodedDataWithImage:image format:format options:nil];
} }
[self _storeImageDataToDisk:data forKey:key error:&writeError]; [self _storeImageDataToDisk:data forKey:key];
} }
if (completionBlock) { if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(writeError); completionBlock();
}); });
} }
}); });
} else { } else {
if (completionBlock) { if (completionBlock) {
completionBlock(nil); completionBlock();
} }
} }
} }
- (BOOL)storeImageDataToDisk:(nullable NSData *)imageData - (void)storeImageDataToDisk:(nullable NSData *)imageData
forKey:(nullable NSString *)key forKey:(nullable NSString *)key {
error:(NSError * _Nullable __autoreleasing * _Nullable)error {
if (!imageData || !key) { if (!imageData || !key) {
return NO; return;
}
__autoreleasing NSError *fileError;
if (!error) {
error = &fileError;
} }
__block BOOL success = YES; dispatch_sync(self.ioQueue, ^{
void(^storeImageDataBlock)(void) = ^{ [self _storeImageDataToDisk:imageData forKey:key];
success = [self _storeImageDataToDisk:imageData forKey:key error:error]; });
};
dispatch_sync(self.ioQueue, storeImageDataBlock);
return success;
} }
// Make sure to call form io queue by caller // Make sure to call form io queue by caller
- (BOOL)_storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key error:(NSError * _Nullable __autoreleasing * _Nonnull)error { - (void)_storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key {
if (!imageData || !key) { if (!imageData || !key) {
return NO; return;
} }
if (![self.fileManager fileExistsAtPath:_diskCachePath]) { if (![self.fileManager fileExistsAtPath:_diskCachePath]) {
if (![self.fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:error]) { if (![self.fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:nil]) {
return NO; return;
} }
} }
@ -349,8 +339,8 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
// NSFileManager's `createFileAtPath:` is used just for old code compatibility and will not trigger any delegate methods, so it's useless for custom NSFileManager at all. // NSFileManager's `createFileAtPath:` is used just for old code compatibility and will not trigger any delegate methods, so it's useless for custom NSFileManager at all.
// And also, NSFileManager's `createFileAtPath:` can only grab underlying POSIX errno, but NSData can grab errors defined in NSCocoaErrorDomain, which is better for user to check. // And also, NSFileManager's `createFileAtPath:` can only grab underlying POSIX errno, but NSData can grab errors defined in NSCocoaErrorDomain, which is better for user to check.
if (![imageData writeToURL:fileURL options:self.config.diskCacheWritingOptions error:error]) { if (![imageData writeToURL:fileURL options:self.config.diskCacheWritingOptions error:nil]) {
return NO; return;
} }
// disable iCloud backup // disable iCloud backup
@ -358,8 +348,6 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
// ignore iCloud backup resource value error // ignore iCloud backup resource value error
[fileURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil]; [fileURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
} }
return YES;
} }
#pragma mark - Query and Retrieve Ops #pragma mark - Query and Retrieve Ops
@ -452,7 +440,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
} }
} }
return nil; return data;
} }
- (nullable UIImage *)diskImageForKey:(nullable NSString *)key { - (nullable UIImage *)diskImageForKey:(nullable NSString *)key {

View File

@ -57,8 +57,7 @@ NSString *kImageTestKey = @"TestImageKey.jpg";
- (void)test05ClearMemoryCache{ - (void)test05ClearMemoryCache{
XCTestExpectation *expectation = [self expectationWithDescription:@"Clear memory cache"]; XCTestExpectation *expectation = [self expectationWithDescription:@"Clear memory cache"];
[[SDImageCache sharedImageCache] storeImage:[self imageForTesting] forKey:kImageTestKey completion:^(NSError * _Nullable error) { [[SDImageCache sharedImageCache] storeImage:[self imageForTesting] forKey:kImageTestKey completion:^{
expect(error).to.beNil();
[[SDImageCache sharedImageCache] clearMemory]; [[SDImageCache sharedImageCache] clearMemory];
expect([[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kImageTestKey]).to.beNil; expect([[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kImageTestKey]).to.beNil;
[[SDImageCache sharedImageCache] diskImageExistsWithKey:kImageTestKey completion:^(BOOL isInCache) { [[SDImageCache sharedImageCache] diskImageExistsWithKey:kImageTestKey completion:^(BOOL isInCache) {
@ -185,8 +184,7 @@ NSString *kImageTestKey = @"TestImageKey.jpg";
- (void)test21InitialDiskCount{ - (void)test21InitialDiskCount{
XCTestExpectation *expectation = [self expectationWithDescription:@"getDiskCount"]; XCTestExpectation *expectation = [self expectationWithDescription:@"getDiskCount"];
[[SDImageCache sharedImageCache] storeImage:[self imageForTesting] forKey:kImageTestKey completion:^(NSError * _Nullable error) { [[SDImageCache sharedImageCache] storeImage:[self imageForTesting] forKey:kImageTestKey completion:^{
expect(error).to.beNil();
expect([[SDImageCache sharedImageCache] getDiskCount]).to.equal(1); expect([[SDImageCache sharedImageCache] getDiskCount]).to.equal(1);
[[SDImageCache sharedImageCache] removeImageForKey:kImageTestKey withCompletion:^{ [[SDImageCache sharedImageCache] removeImageForKey:kImageTestKey withCompletion:^{
[expectation fulfill]; [expectation fulfill];
@ -207,8 +205,7 @@ NSString *kImageTestKey = @"TestImageKey.jpg";
- (void)test33CachePathForExistingKey{ - (void)test33CachePathForExistingKey{
XCTestExpectation *expectation = [self expectationWithDescription:@"cachePathForKey inPath"]; XCTestExpectation *expectation = [self expectationWithDescription:@"cachePathForKey inPath"];
[[SDImageCache sharedImageCache] storeImage:[self imageForTesting] forKey:kImageTestKey completion:^(NSError * _Nullable error) { [[SDImageCache sharedImageCache] storeImage:[self imageForTesting] forKey:kImageTestKey completion:^{
expect(error).to.beNil();
NSString *path = [[SDImageCache sharedImageCache] cachePathForKey:kImageTestKey]; NSString *path = [[SDImageCache sharedImageCache] cachePathForKey:kImageTestKey];
expect(path).notTo.beNil; expect(path).notTo.beNil;
[[SDImageCache sharedImageCache] removeImageForKey:kImageTestKey withCompletion:^{ [[SDImageCache sharedImageCache] removeImageForKey:kImageTestKey withCompletion:^{
@ -237,7 +234,7 @@ NSString *kImageTestKey = @"TestImageKey.jpg";
UIImage *image = [[UIImage alloc] initWithContentsOfFile:[self testImagePath]]; UIImage *image = [[UIImage alloc] initWithContentsOfFile:[self testImagePath]];
NSData *imageData = UIImageJPEGRepresentation(image, 1.0); NSData *imageData = UIImageJPEGRepresentation(image, 1.0);
[[SDImageCache sharedImageCache] storeImageDataToDisk:imageData forKey:kImageTestKey error:nil]; [[SDImageCache sharedImageCache] storeImageDataToDisk:imageData forKey:kImageTestKey];
UIImage *storedImageFromMemory = [[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kImageTestKey]; UIImage *storedImageFromMemory = [[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kImageTestKey];
expect(storedImageFromMemory).to.equal(nil); expect(storedImageFromMemory).to.equal(nil);
@ -269,7 +266,7 @@ NSString *kImageTestKey = @"TestImageKey.jpg";
UIImage *image = [[UIImage alloc] initWithContentsOfFile:testImagePath]; UIImage *image = [[UIImage alloc] initWithContentsOfFile:testImagePath];
NSString *key = @"TestPNGImageEncodedToDataAndRetrieveToJPEG"; NSString *key = @"TestPNGImageEncodedToDataAndRetrieveToJPEG";
[cache storeImage:image imageData:nil forKey:key toDisk:YES completion:^(NSError * _Nullable error) { [cache storeImage:image imageData:nil forKey:key toDisk:YES completion:^{
[cache clearMemory]; [cache clearMemory];
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector" #pragma clang diagnostic ignored "-Wundeclared-selector"
@ -309,35 +306,19 @@ NSString *kImageTestKey = @"TestImageKey.jpg";
} }
#endif #endif
- (void)test41StoreImageDataToDiskWithError { - (void)test41StoreImageDataToDiskWithCustomFileManager {
NSData *imageData = [NSData dataWithContentsOfFile:[self testImagePath]]; NSData *imageData = [NSData dataWithContentsOfFile:[self testImagePath]];
NSError *targetError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteNoPermissionError userInfo:nil]; NSError *targetError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteNoPermissionError userInfo:nil];
NSError *error = nil;
SDMockFileManager *fileManager = [[SDMockFileManager alloc] init]; SDMockFileManager *fileManager = [[SDMockFileManager alloc] init];
fileManager.mockSelectors = @{NSStringFromSelector(@selector(createDirectoryAtPath:withIntermediateDirectories:attributes:error:)) : targetError}; fileManager.mockSelectors = @{NSStringFromSelector(@selector(createDirectoryAtPath:withIntermediateDirectories:attributes:error:)) : targetError};
SDImageCache *cache = [[SDImageCache alloc] initWithNamespace:@"test" expect(fileManager.lastError).to.beNil();
diskCacheDirectory:@"/"
fileManager:fileManager];
[cache storeImageDataToDisk:imageData
forKey:kImageTestKey
error:&error];
XCTAssertEqual(error.code, NSFileWriteNoPermissionError); // This disk cache path creation will be mocked with error.
} SDImageCache *cache = [[SDImageCache alloc] initWithNamespace:@"test" diskCacheDirectory:@"/" fileManager:fileManager];
- (void)test42StoreImageDataToDiskWithoutError {
NSData *imageData = [NSData dataWithContentsOfFile:[self testImagePath]];
NSError *error = nil;
SDMockFileManager *fileManager = [[SDMockFileManager alloc] init];
fileManager.mockSelectors = @{NSStringFromSelector(@selector(createDirectoryAtPath:withIntermediateDirectories:attributes:error:)) : [NSNull null]};
SDImageCache *cache = [[SDImageCache alloc] initWithNamespace:@"test"
diskCacheDirectory:@"/"
fileManager:fileManager];
[cache storeImageDataToDisk:imageData [cache storeImageDataToDisk:imageData
forKey:kImageTestKey forKey:kImageTestKey];
error:&error]; expect(fileManager.lastError).equal(targetError);
XCTAssertNil(error);
} }
#pragma mark Helper methods #pragma mark Helper methods

View File

@ -11,6 +11,8 @@
// This is a mock class to provide custom error for methods // This is a mock class to provide custom error for methods
@interface SDMockFileManager : NSFileManager @interface SDMockFileManager : NSFileManager
@property (nonatomic, strong, readonly, nullable) NSError *lastError;
@property (nonatomic, copy, nullable) NSDictionary<NSString *, NSError *> *mockSelectors; // used to specify mocked selectors which will return NO with specify error instead of normal process. If you specify a NSNull, will use nil instead. @property (nonatomic, copy, nullable) NSDictionary<NSString *, NSError *> *mockSelectors; // used to specify mocked selectors which will return NO with specify error instead of normal process. If you specify a NSNull, will use nil instead.
@end @end

View File

@ -10,11 +10,14 @@
@interface SDMockFileManager () @interface SDMockFileManager ()
@property (nonatomic, strong, nullable) NSError *lastError;
@end @end
@implementation SDMockFileManager @implementation SDMockFileManager
- (BOOL)createDirectoryAtPath:(NSString *)path withIntermediateDirectories:(BOOL)createIntermediates attributes:(NSDictionary<NSFileAttributeKey,id> *)attributes error:(NSError * _Nullable __autoreleasing *)error { - (BOOL)createDirectoryAtPath:(NSString *)path withIntermediateDirectories:(BOOL)createIntermediates attributes:(NSDictionary<NSFileAttributeKey,id> *)attributes error:(NSError * _Nullable __autoreleasing *)error {
self.lastError = nil;
NSError *mockError = [self.mockSelectors objectForKey:NSStringFromSelector(_cmd)]; NSError *mockError = [self.mockSelectors objectForKey:NSStringFromSelector(_cmd)];
if ([mockError isEqual:[NSNull null]]) { if ([mockError isEqual:[NSNull null]]) {
if (error) { if (error) {
@ -25,6 +28,7 @@
if (error) { if (error) {
*error = mockError; *error = mockError;
} }
self.lastError = mockError;
return NO; return NO;
} else { } else {
return [super createDirectoryAtPath:path withIntermediateDirectories:createIntermediates attributes:attributes error:error]; return [super createDirectoryAtPath:path withIntermediateDirectories:createIntermediates attributes:attributes error:error];