From 9f770b6c193df438cded37016d9aacd7e6819a68 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Fri, 13 Apr 2018 18:52:46 +0800 Subject: [PATCH 1/5] Refactor to support custom image cache used for web manager. Supports caches manager with multiple caches and op policy --- .../SDWebImage Demo/MasterViewController.m | 3 +- SDWebImage.xcodeproj/project.pbxproj | 56 ++ SDWebImage/SDImageCache.h | 23 +- SDWebImage/SDImageCache.m | 164 ++++- SDWebImage/SDWebImageCache.h | 109 ++++ SDWebImage/SDWebImageCache.m | 9 + SDWebImage/SDWebImageCachesManager.h | 77 +++ SDWebImage/SDWebImageCachesManager.m | 582 ++++++++++++++++++ SDWebImage/SDWebImageManager.h | 53 +- SDWebImage/SDWebImageManager.m | 102 ++- WebImage/SDWebImage.h | 2 + 11 files changed, 1065 insertions(+), 115 deletions(-) create mode 100644 SDWebImage/SDWebImageCache.h create mode 100644 SDWebImage/SDWebImageCache.m create mode 100644 SDWebImage/SDWebImageCachesManager.h create mode 100644 SDWebImage/SDWebImageCachesManager.m diff --git a/Examples/SDWebImage Demo/MasterViewController.m b/Examples/SDWebImage Demo/MasterViewController.m index 58469d2a..8ead3031 100644 --- a/Examples/SDWebImage Demo/MasterViewController.m +++ b/Examples/SDWebImage Demo/MasterViewController.m @@ -85,8 +85,7 @@ - (void)flushCache { - [SDWebImageManager.sharedManager.imageCache clearMemory]; - [SDWebImageManager.sharedManager.imageCache clearDiskOnCompletion:nil]; + [SDWebImageManager.sharedManager.imageCache clearWithCacheType:SDImageCacheTypeAll completion:nil]; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation diff --git a/SDWebImage.xcodeproj/project.pbxproj b/SDWebImage.xcodeproj/project.pbxproj index 8b067e34..7a367417 100644 --- a/SDWebImage.xcodeproj/project.pbxproj +++ b/SDWebImage.xcodeproj/project.pbxproj @@ -496,6 +496,30 @@ 32CF1C101FA496B000004BD1 /* SDWebImageCoderHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 32CF1C061FA496B000004BD1 /* SDWebImageCoderHelper.m */; }; 32CF1C111FA496B000004BD1 /* SDWebImageCoderHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 32CF1C061FA496B000004BD1 /* SDWebImageCoderHelper.m */; }; 32CF1C121FA496B000004BD1 /* SDWebImageCoderHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 32CF1C061FA496B000004BD1 /* SDWebImageCoderHelper.m */; }; + 32D1221E2080B2EB003685A3 /* SDWebImageCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 32D1221A2080B2EB003685A3 /* SDWebImageCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 32D1221F2080B2EB003685A3 /* SDWebImageCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 32D1221A2080B2EB003685A3 /* SDWebImageCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 32D122202080B2EB003685A3 /* SDWebImageCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 32D1221A2080B2EB003685A3 /* SDWebImageCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 32D122212080B2EB003685A3 /* SDWebImageCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 32D1221A2080B2EB003685A3 /* SDWebImageCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 32D122222080B2EB003685A3 /* SDWebImageCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 32D1221A2080B2EB003685A3 /* SDWebImageCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 32D122232080B2EB003685A3 /* SDWebImageCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 32D1221A2080B2EB003685A3 /* SDWebImageCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 32D122242080B2EB003685A3 /* SDWebImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D1221B2080B2EB003685A3 /* SDWebImageCache.m */; }; + 32D122252080B2EB003685A3 /* SDWebImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D1221B2080B2EB003685A3 /* SDWebImageCache.m */; }; + 32D122262080B2EB003685A3 /* SDWebImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D1221B2080B2EB003685A3 /* SDWebImageCache.m */; }; + 32D122272080B2EB003685A3 /* SDWebImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D1221B2080B2EB003685A3 /* SDWebImageCache.m */; }; + 32D122282080B2EB003685A3 /* SDWebImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D1221B2080B2EB003685A3 /* SDWebImageCache.m */; }; + 32D122292080B2EB003685A3 /* SDWebImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D1221B2080B2EB003685A3 /* SDWebImageCache.m */; }; + 32D1222A2080B2EB003685A3 /* SDWebImageCachesManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D1221C2080B2EB003685A3 /* SDWebImageCachesManager.m */; }; + 32D1222B2080B2EB003685A3 /* SDWebImageCachesManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D1221C2080B2EB003685A3 /* SDWebImageCachesManager.m */; }; + 32D1222C2080B2EB003685A3 /* SDWebImageCachesManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D1221C2080B2EB003685A3 /* SDWebImageCachesManager.m */; }; + 32D1222D2080B2EB003685A3 /* SDWebImageCachesManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D1221C2080B2EB003685A3 /* SDWebImageCachesManager.m */; }; + 32D1222E2080B2EB003685A3 /* SDWebImageCachesManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D1221C2080B2EB003685A3 /* SDWebImageCachesManager.m */; }; + 32D1222F2080B2EB003685A3 /* SDWebImageCachesManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 32D1221C2080B2EB003685A3 /* SDWebImageCachesManager.m */; }; + 32D122302080B2EB003685A3 /* SDWebImageCachesManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 32D1221D2080B2EB003685A3 /* SDWebImageCachesManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 32D122312080B2EB003685A3 /* SDWebImageCachesManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 32D1221D2080B2EB003685A3 /* SDWebImageCachesManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 32D122322080B2EB003685A3 /* SDWebImageCachesManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 32D1221D2080B2EB003685A3 /* SDWebImageCachesManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 32D122332080B2EB003685A3 /* SDWebImageCachesManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 32D1221D2080B2EB003685A3 /* SDWebImageCachesManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 32D122342080B2EB003685A3 /* SDWebImageCachesManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 32D1221D2080B2EB003685A3 /* SDWebImageCachesManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 32D122352080B2EB003685A3 /* SDWebImageCachesManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 32D1221D2080B2EB003685A3 /* SDWebImageCachesManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32EB6D8E206D132E005CAEF6 /* SDAnimatedImageRep.m in Sources */ = {isa = PBXBuildFile; fileRef = 320224BA203979BA00E9F285 /* SDAnimatedImageRep.m */; }; 32EB6D8F206D132E005CAEF6 /* SDAnimatedImageRep.m in Sources */ = {isa = PBXBuildFile; fileRef = 320224BA203979BA00E9F285 /* SDAnimatedImageRep.m */; }; 32EB6D90206D132E005CAEF6 /* SDAnimatedImageRep.m in Sources */ = {isa = PBXBuildFile; fileRef = 320224BA203979BA00E9F285 /* SDAnimatedImageRep.m */; }; @@ -1558,6 +1582,10 @@ 32C0FDE02013426C001B8F2D /* SDWebImageIndicator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageIndicator.m; sourceTree = ""; }; 32CF1C051FA496B000004BD1 /* SDWebImageCoderHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDWebImageCoderHelper.h; sourceTree = ""; }; 32CF1C061FA496B000004BD1 /* SDWebImageCoderHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageCoderHelper.m; sourceTree = ""; }; + 32D1221A2080B2EB003685A3 /* SDWebImageCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebImageCache.h; sourceTree = ""; }; + 32D1221B2080B2EB003685A3 /* SDWebImageCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageCache.m; sourceTree = ""; }; + 32D1221C2080B2EB003685A3 /* SDWebImageCachesManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageCachesManager.m; sourceTree = ""; }; + 32D1221D2080B2EB003685A3 /* SDWebImageCachesManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebImageCachesManager.h; sourceTree = ""; }; 32F21B4F20788D8C0036B1D5 /* SDWebImageDownloaderRequestModifier.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDWebImageDownloaderRequestModifier.h; sourceTree = ""; }; 32F21B5020788D8C0036B1D5 /* SDWebImageDownloaderRequestModifier.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageDownloaderRequestModifier.m; sourceTree = ""; }; 32F7C06D2030114C00873181 /* SDWebImageTransformer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDWebImageTransformer.h; sourceTree = ""; }; @@ -2076,6 +2104,10 @@ 328BB6C02082581100760D6C /* SDMemoryCache.m */, 328BB6BD2082581100760D6C /* SDDiskCache.h */, 328BB6BE2082581100760D6C /* SDDiskCache.m */, + 32D1221A2080B2EB003685A3 /* SDWebImageCache.h */, + 32D1221B2080B2EB003685A3 /* SDWebImageCache.m */, + 32D1221D2080B2EB003685A3 /* SDWebImageCachesManager.h */, + 32D1221C2080B2EB003685A3 /* SDWebImageCachesManager.m */, ); name = Cache; sourceTree = ""; @@ -2294,6 +2326,7 @@ 323F8B531F38EF770092B609 /* backward_references_enc.h in Headers */, 4317395A1CDFC8B70008FEB9 /* mux_types.h in Headers */, 431739561CDFC8B70008FEB9 /* demux.h in Headers */, + 32D122212080B2EB003685A3 /* SDWebImageCache.h in Headers */, 32B9B53A206ED4230026769D /* SDWebImageDownloaderConfig.h in Headers */, 328BB6AD2081FEE500760D6C /* SDWebImageCacheSerializer.h in Headers */, 80377C4A1F2F666300F89830 /* bit_writer_utils.h in Headers */, @@ -2362,6 +2395,7 @@ 3248476C201775F600AF9E5A /* SDAnimatedImageView.h in Headers */, 80377C5F1F2F666300F89830 /* utils.h in Headers */, 80377C5B1F2F666300F89830 /* rescaler_utils.h in Headers */, + 32D122332080B2EB003685A3 /* SDWebImageCachesManager.h in Headers */, 323F8BF91F38EF770092B609 /* animi.h in Headers */, 32F7C0872030719600873181 /* UIImage+Transform.h in Headers */, 80377C4F1F2F666300F89830 /* filters_utils.h in Headers */, @@ -2389,6 +2423,7 @@ 80377C1D1F2F666300F89830 /* huffman_encode_utils.h in Headers */, 321E60B11F38E90100405457 /* SDWebImageWebPCoder.h in Headers */, 80377E9A1F2F66D400F89830 /* common_dec.h in Headers */, + 32D1221F2080B2EB003685A3 /* SDWebImageCache.h in Headers */, 327054D5206CD8B3006EA328 /* SDWebImageAPNGCoder.h in Headers */, 328BB6AB2081FEE500760D6C /* SDWebImageCacheSerializer.h in Headers */, 32B9B538206ED4230026769D /* SDWebImageDownloaderConfig.h in Headers */, @@ -2429,6 +2464,7 @@ 80377C271F2F666300F89830 /* rescaler_utils.h in Headers */, 323F8B511F38EF770092B609 /* backward_references_enc.h in Headers */, 325312C9200F09910046BF1E /* SDWebImageTransition.h in Headers */, + 32D122312080B2EB003685A3 /* SDWebImageCachesManager.h in Headers */, 43A918651D8308FE00B3925F /* SDImageCacheConfig.h in Headers */, 4314D1741D0E0E3B004B36C9 /* types.h in Headers */, 4314D1761D0E0E3B004B36C9 /* decode.h in Headers */, @@ -2510,6 +2546,7 @@ 80377E211F2F66A800F89830 /* neon.h in Headers */, 80377C711F2F666400F89830 /* quant_levels_utils.h in Headers */, 323F8B541F38EF770092B609 /* backward_references_enc.h in Headers */, + 32D122342080B2EB003685A3 /* SDWebImageCachesManager.h in Headers */, 32F7C0882030719600873181 /* UIImage+Transform.h in Headers */, 43A62A1F1D0E0A800089D7DD /* mux.h in Headers */, 431BB6E91D06D2C1006A3455 /* SDWebImageDownloaderOperation.h in Headers */, @@ -2553,6 +2590,7 @@ 32F7C0732030114C00873181 /* SDWebImageTransformer.h in Headers */, 431BB6FA1D06D2C1006A3455 /* SDWebImageDownloader.h in Headers */, 3248476D201775F600AF9E5A /* SDAnimatedImageView.h in Headers */, + 32D122222080B2EB003685A3 /* SDWebImageCache.h in Headers */, 80377DF51F2F66A800F89830 /* common_sse2.h in Headers */, 323F8BDC1F38EF770092B609 /* vp8i_enc.h in Headers */, 80377ED21F2F66D500F89830 /* vp8i_dec.h in Headers */, @@ -2573,6 +2611,7 @@ 32F7C0892030719600873181 /* UIImage+Transform.h in Headers */, 80377EDA1F2F66D500F89830 /* common_dec.h in Headers */, 80377EE61F2F66D500F89830 /* webpi_dec.h in Headers */, + 32D122232080B2EB003685A3 /* SDWebImageCache.h in Headers */, 32B9B53C206ED4230026769D /* SDWebImageDownloaderConfig.h in Headers */, 328BB6AF2081FEE500760D6C /* SDWebImageCacheSerializer.h in Headers */, 4397D2BA1D0DDD8C00BB2784 /* demux.h in Headers */, @@ -2641,6 +2680,7 @@ 321E608B1F38E8C800405457 /* SDWebImageCoder.h in Headers */, 323F8B731F38EF770092B609 /* delta_palettization_enc.h in Headers */, 321E60C31F38E91700405457 /* UIImage+ForceDecode.h in Headers */, + 32D122352080B2EB003685A3 /* SDWebImageCachesManager.h in Headers */, 32484768201775F600AF9E5A /* SDAnimatedImageView+WebCache.h in Headers */, 80377E561F2F66A800F89830 /* lossless_common.h in Headers */, 4397D2E91D0DDD8C00BB2784 /* UIImage+WebP.h in Headers */, @@ -2669,6 +2709,7 @@ 323F8B521F38EF770092B609 /* backward_references_enc.h in Headers */, 4317394F1CDFC8B70008FEB9 /* demux.h in Headers */, 43CE757D1CFE9427006C64D0 /* FLAnimatedImageView.h in Headers */, + 32D122202080B2EB003685A3 /* SDWebImageCache.h in Headers */, 32B9B539206ED4230026769D /* SDWebImageDownloaderConfig.h in Headers */, 328BB6AC2081FEE500760D6C /* SDWebImageCacheSerializer.h in Headers */, 80377C301F2F666300F89830 /* bit_writer_utils.h in Headers */, @@ -2737,6 +2778,7 @@ 3248476B201775F600AF9E5A /* SDAnimatedImageView.h in Headers */, 4A2CAE311AB4BB7500B6BC39 /* UIImage+WebP.h in Headers */, 323F8BF81F38EF770092B609 /* animi.h in Headers */, + 32D122322080B2EB003685A3 /* SDWebImageCachesManager.h in Headers */, 80377C351F2F666300F89830 /* filters_utils.h in Headers */, 32F7C0862030719600873181 /* UIImage+Transform.h in Headers */, 80377C321F2F666300F89830 /* color_cache_utils.h in Headers */, @@ -2790,6 +2832,7 @@ 431738BD1CDFC2660008FEB9 /* decode.h in Headers */, 80377D0B1F2F66A100F89830 /* mips_macro.h in Headers */, 329A18591FFF5DFD008C9A2F /* UIImage+WebCache.h in Headers */, + 32D122302080B2EB003685A3 /* SDWebImageCachesManager.h in Headers */, 5376131A155AD0D5005750A4 /* SDWebImageDownloader.h in Headers */, 328BB6CD2082581100760D6C /* SDMemoryCache.h in Headers */, 4369C2771D9807EC007E863A /* UIView+WebCache.h in Headers */, @@ -2836,6 +2879,7 @@ 323F8B6E1F38EF770092B609 /* delta_palettization_enc.h in Headers */, 438096721CDFC08200DC626B /* MKAnnotationView+WebCache.h in Headers */, 43CE757C1CFE9427006C64D0 /* FLAnimatedImageView.h in Headers */, + 32D1221E2080B2EB003685A3 /* SDWebImageCache.h in Headers */, 80377E8A1F2F66D000F89830 /* common_dec.h in Headers */, AB615303192DA24600A2D8E9 /* UIView+WebCacheOperation.h in Headers */, 323F8B501F38EF770092B609 /* backward_references_enc.h in Headers */, @@ -3151,6 +3195,7 @@ 80377DDE1F2F66A700F89830 /* rescaler_mips32.c in Sources */, 80377DCA1F2F66A700F89830 /* filters_sse2.c in Sources */, 80377EBE1F2F66D500F89830 /* quant_dec.c in Sources */, + 32D1222D2080B2EB003685A3 /* SDWebImageCachesManager.m in Sources */, 80377DB61F2F66A700F89830 /* dec_clip_tables.c in Sources */, 80377C5E1F2F666300F89830 /* utils.c in Sources */, 32B9B540206ED4230026769D /* SDWebImageDownloaderConfig.m in Sources */, @@ -3193,6 +3238,7 @@ 80377DB51F2F66A700F89830 /* cpu.c in Sources */, 80377EC51F2F66D500F89830 /* webp_dec.c in Sources */, 80377DD61F2F66A700F89830 /* lossless_neon.c in Sources */, + 32D122272080B2EB003685A3 /* SDWebImageCache.m in Sources */, 00733A5C1BC4880000A5A117 /* UIButton+WebCache.m in Sources */, 80377EC01F2F66D500F89830 /* vp8_dec.c in Sources */, 80377C521F2F666300F89830 /* huffman_utils.c in Sources */, @@ -3276,6 +3322,7 @@ 4314D1341D0E0E3B004B36C9 /* UIImage+WebP.m in Sources */, 80377D3D1F2F66A700F89830 /* filters_mips_dsp_r2.c in Sources */, 323F8B751F38EF770092B609 /* filter_enc.c in Sources */, + 32D122252080B2EB003685A3 /* SDWebImageCache.m in Sources */, 80377D401F2F66A700F89830 /* filters_sse2.c in Sources */, 80377D1E1F2F66A700F89830 /* alpha_processing_mips_dsp_r2.c in Sources */, 80377D291F2F66A700F89830 /* cost_sse2.c in Sources */, @@ -3318,6 +3365,7 @@ 80377D2F1F2F66A700F89830 /* dec_msa.c in Sources */, 323F8C151F38EF770092B609 /* muxinternal.c in Sources */, 80377D571F2F66A700F89830 /* rescaler_sse2.c in Sources */, + 32D1222B2080B2EB003685A3 /* SDWebImageCachesManager.m in Sources */, 43C892A11D9D6DDC0022038D /* demux.c in Sources */, 80377C131F2F666300F89830 /* bit_reader_utils.c in Sources */, 80377E9C1F2F66D400F89830 /* idec_dec.c in Sources */, @@ -3437,6 +3485,7 @@ 323F8BE21F38EF770092B609 /* vp8l_enc.c in Sources */, 431BB6A31D06D2C1006A3455 /* UIImageView+WebCache.m in Sources */, 80377E0C1F2F66A800F89830 /* filters_mips_dsp_r2.c in Sources */, + 32D122282080B2EB003685A3 /* SDWebImageCache.m in Sources */, 323F8B781F38EF770092B609 /* filter_enc.c in Sources */, 4369C2821D9807EC007E863A /* UIView+WebCache.m in Sources */, 80377E0F1F2F66A800F89830 /* filters_sse2.c in Sources */, @@ -3479,6 +3528,7 @@ 323F8BBE1F38EF770092B609 /* predictor_enc.c in Sources */, 80377E261F2F66A800F89830 /* rescaler_sse2.c in Sources */, 323F8C181F38EF770092B609 /* muxinternal.c in Sources */, + 32D1222E2080B2EB003685A3 /* SDWebImageCachesManager.m in Sources */, 80377C741F2F666400F89830 /* rescaler_utils.c in Sources */, 431BB6B11D06D2C1006A3455 /* UIView+WebCacheOperation.m in Sources */, 80377DF01F2F66A800F89830 /* alpha_processing_sse41.c in Sources */, @@ -3616,6 +3666,7 @@ 321E60C91F38E91700405457 /* UIImage+ForceDecode.m in Sources */, 80377E551F2F66A800F89830 /* filters.c in Sources */, 80377E731F2F66A800F89830 /* yuv_mips32.c in Sources */, + 32D1222F2080B2EB003685A3 /* SDWebImageCachesManager.m in Sources */, 4397D2911D0DDD8C00BB2784 /* MKAnnotationView+WebCache.m in Sources */, 4397D2921D0DDD8C00BB2784 /* SDWebImagePrefetcher.m in Sources */, 323F8BBF1F38EF770092B609 /* predictor_enc.c in Sources */, @@ -3628,6 +3679,7 @@ 80377E381F2F66A800F89830 /* argb_sse2.c in Sources */, 323F8B9B1F38EF770092B609 /* near_lossless_enc.c in Sources */, 32F21B5C20788D8C0036B1D5 /* SDWebImageDownloaderRequestModifier.m in Sources */, + 32D122292080B2EB003685A3 /* SDWebImageCache.m in Sources */, 80377E3B1F2F66A800F89830 /* cost_mips_dsp_r2.c in Sources */, 4397D29B1D0DDD8C00BB2784 /* SDWebImageDownloader.m in Sources */, 80377E711F2F66A800F89830 /* upsampling.c in Sources */, @@ -3801,6 +3853,7 @@ 80377C441F2F666300F89830 /* utils.c in Sources */, 80377D8D1F2F66A700F89830 /* lossless_enc_sse41.c in Sources */, 80377EAE1F2F66D400F89830 /* quant_dec.c in Sources */, + 32D1222C2080B2EB003685A3 /* SDWebImageCachesManager.m in Sources */, 80377D6E1F2F66A700F89830 /* cost_sse2.c in Sources */, 80377D991F2F66A700F89830 /* rescaler_mips32.c in Sources */, 32B9B53F206ED4230026769D /* SDWebImageDownloaderConfig.m in Sources */, @@ -3843,6 +3896,7 @@ 80377D8C1F2F66A700F89830 /* lossless_enc_sse2.c in Sources */, 4A2CAE2C1AB4BB7500B6BC39 /* UIButton+WebCache.m in Sources */, 80377EB51F2F66D400F89830 /* webp_dec.c in Sources */, + 32D122262080B2EB003685A3 /* SDWebImageCache.m in Sources */, 80377D701F2F66A700F89830 /* cpu.c in Sources */, 80377D911F2F66A700F89830 /* lossless_neon.c in Sources */, 80377EB01F2F66D400F89830 /* vp8_dec.c in Sources */, @@ -3966,6 +4020,7 @@ 80377D031F2F66A100F89830 /* lossless_enc_sse41.c in Sources */, 80377E8E1F2F66D000F89830 /* quant_dec.c in Sources */, 80377CE41F2F66A100F89830 /* cost_sse2.c in Sources */, + 32D1222A2080B2EB003685A3 /* SDWebImageCachesManager.m in Sources */, 80377D0F1F2F66A100F89830 /* rescaler_mips32.c in Sources */, 323F8C081F38EF770092B609 /* muxedit.c in Sources */, 32B9B53D206ED4230026769D /* SDWebImageDownloaderConfig.m in Sources */, @@ -4008,6 +4063,7 @@ A18A6CC9172DC28500419892 /* UIImage+GIF.m in Sources */, 80377E951F2F66D000F89830 /* webp_dec.c in Sources */, 80377CE61F2F66A100F89830 /* cpu.c in Sources */, + 32D122242080B2EB003685A3 /* SDWebImageCache.m in Sources */, 80377D071F2F66A100F89830 /* lossless_neon.c in Sources */, 80377E901F2F66D000F89830 /* vp8_dec.c in Sources */, 80377C041F2F665300F89830 /* huffman_utils.c in Sources */, diff --git a/SDWebImage/SDImageCache.h b/SDWebImage/SDImageCache.h index 96860683..c22bff89 100644 --- a/SDWebImage/SDImageCache.h +++ b/SDWebImage/SDImageCache.h @@ -10,21 +10,7 @@ #import "SDWebImageCompat.h" #import "SDWebImageDefine.h" #import "SDImageCacheConfig.h" - -typedef NS_ENUM(NSInteger, SDImageCacheType) { - /** - * The image wasn't available the SDWebImage caches, but was downloaded from the web. - */ - SDImageCacheTypeNone, - /** - * The image was obtained from the disk cache. - */ - SDImageCacheTypeDisk, - /** - * The image was obtained from the memory cache. - */ - SDImageCacheTypeMemory -}; +#import "SDWebImageCache.h" typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) { /** @@ -319,3 +305,10 @@ typedef NSString * _Nullable (^SDImageCacheAdditionalCachePathBlock)(NSString * - (void)calculateSizeWithCompletionBlock:(nullable SDWebImageCalculateSizeBlock)completionBlock; @end + +/** + * SDImageCache is the built-in image cache implementation for web image manager. It adopts `SDWebImageCache` protocol to provide the function for web image manager to use for image loading process. + */ +@interface SDImageCache (SDWebImageCache) + +@end diff --git a/SDWebImage/SDImageCache.m b/SDWebImage/SDImageCache.m index 41756be1..b00746af 100644 --- a/SDWebImage/SDImageCache.m +++ b/SDWebImage/SDImageCache.m @@ -145,6 +145,15 @@ forKey:(nullable NSString *)key toDisk:(BOOL)toDisk completion:(nullable SDWebImageNoParamsBlock)completionBlock { + return [self storeImage:image imageData:imageData forKey:key toMemory:YES toDisk:toDisk completion:completionBlock]; +} + +- (void)storeImage:(nullable UIImage *)image + imageData:(nullable NSData *)imageData + forKey:(nullable NSString *)key + toMemory:(BOOL)toMemory + toDisk:(BOOL)toDisk + completion:(nullable SDWebImageNoParamsBlock)completionBlock { if (!image || !key) { if (completionBlock) { completionBlock(); @@ -152,7 +161,7 @@ return; } // if memory cache is enabled - if (self.config.shouldCacheImagesInMemory) { + if (toMemory && self.config.shouldCacheImagesInMemory) { NSUInteger cost = SDMemoryCacheCostForImage(image); [self.memCache setObject:image forKey:key cost:cost]; } @@ -426,11 +435,15 @@ } - (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion { + [self removeImageForKey:key fromMemory:YES fromDisk:fromDisk withCompletion:completion]; +} + +- (void)removeImageForKey:(nullable NSString *)key fromMemory:(BOOL)fromMemory fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion { if (key == nil) { return; } - if (self.config.shouldCacheImagesInMemory) { + if (fromMemory && self.config.shouldCacheImagesInMemory) { [self.memCache removeObjectForKey:key]; } @@ -544,3 +557,150 @@ @end +@implementation SDImageCache (SDWebImageCache) + +#pragma mark - SDWebImageCache + +- (id)queryImageForKey:(NSString *)key options:(SDWebImageOptions)options context:(nullable SDWebImageContext *)context completion:(nullable SDImageCacheQueryCompletionBlock)completionBlock { + SDImageCacheOptions cacheOptions = 0; + if (options & SDWebImageQueryDataWhenInMemory) cacheOptions |= SDImageCacheQueryDataWhenInMemory; + if (options & SDWebImageQueryDiskSync) cacheOptions |= SDImageCacheQueryDiskSync; + if (options & SDWebImageTransformAnimatedImage) cacheOptions |= SDImageCacheTransformAnimatedImage; + if (options & SDWebImageDecodeFirstFrameOnly) cacheOptions |= SDImageCacheDecodeFirstFrameOnly; + if (options & SDWebImagePreloadAllFrames) cacheOptions |= SDImageCachePreloadAllFrames; + return [self queryCacheOperationForKey:key options:cacheOptions context:context done:completionBlock]; +} + +- (void)storeImage:(UIImage *)image imageData:(NSData *)imageData forKey:(nullable NSString *)key cacheType:(SDImageCacheType)cacheType completion:(nullable SDWebImageNoParamsBlock)completionBlock { + switch (cacheType) { + case SDImageCacheTypeNone: { + [self storeImage:image imageData:imageData forKey:key toMemory:NO toDisk:NO completion:completionBlock]; + } + break; + case SDImageCacheTypeMemory: { + [self storeImage:image imageData:imageData forKey:key toMemory:YES toDisk:NO completion:completionBlock]; + } + break; + case SDImageCacheTypeDisk: { + [self storeImage:image imageData:imageData forKey:key toMemory:NO toDisk:YES completion:completionBlock]; + } + break; + case SDImageCacheTypeAll: { + [self storeImage:image imageData:imageData forKey:key toMemory:YES toDisk:YES completion:completionBlock]; + } + break; + default: { + if (completionBlock) { + completionBlock(); + } + } + break; + } +} + +- (void)removeImageForKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(nullable SDWebImageNoParamsBlock)completionBlock { + switch (cacheType) { + case SDImageCacheTypeNone: { + [self removeImageForKey:key fromMemory:NO fromDisk:NO withCompletion:completionBlock]; + } + break; + case SDImageCacheTypeMemory: { + [self removeImageForKey:key fromMemory:YES fromDisk:NO withCompletion:completionBlock]; + } + break; + case SDImageCacheTypeDisk: { + [self removeImageForKey:key fromMemory:NO fromDisk:YES withCompletion:completionBlock]; + } + break; + case SDImageCacheTypeAll: { + [self removeImageForKey:key fromMemory:YES fromDisk:YES withCompletion:completionBlock]; + } + break; + default: { + if (completionBlock) { + completionBlock(); + } + } + break; + } +} + +- (void)containsImageForKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(nullable SDImageCacheContainsCompletionBlock)completionBlock { + switch (cacheType) { + case SDImageCacheTypeNone: { + if (completionBlock) { + completionBlock(SDImageCacheTypeNone); + } + } + break; + case SDImageCacheTypeMemory: { + BOOL isInMemoryCache = ([self imageFromMemoryCacheForKey:key] != nil); + if (completionBlock) { + completionBlock(isInMemoryCache ? SDImageCacheTypeMemory : SDImageCacheTypeNone); + } + } + break; + case SDImageCacheTypeDisk: { + [self diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) { + if (completionBlock) { + completionBlock(isInDiskCache ? SDImageCacheTypeDisk : SDImageCacheTypeNone); + } + }]; + } + break; + case SDImageCacheTypeAll: { + BOOL isInMemoryCache = ([self imageFromMemoryCacheForKey:key] != nil); + if (isInMemoryCache) { + if (completionBlock) { + completionBlock(SDImageCacheTypeMemory); + } + return; + } + [self diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) { + if (completionBlock) { + completionBlock(isInDiskCache ? SDImageCacheTypeDisk : SDImageCacheTypeNone); + } + }]; + } + break; + default: + if (completionBlock) { + completionBlock(SDImageCacheTypeNone); + } + break; + } +} + +- (void)clearWithCacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock { + switch (cacheType) { + case SDImageCacheTypeNone: { + return; + } + break; + case SDImageCacheTypeMemory: { + [self clearMemory]; + if (completionBlock) { + completionBlock(); + } + } + break; + case SDImageCacheTypeDisk: { + [self clearDiskOnCompletion:completionBlock]; + } + break; + case SDImageCacheTypeAll: { + [self clearMemory]; + [self clearDiskOnCompletion:completionBlock]; + } + break; + default: { + if (completionBlock) { + completionBlock(); + } + } + break; + } +} + +@end + diff --git a/SDWebImage/SDWebImageCache.h b/SDWebImage/SDWebImageCache.h new file mode 100644 index 00000000..ed101362 --- /dev/null +++ b/SDWebImage/SDWebImageCache.h @@ -0,0 +1,109 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import +#import "SDWebImageCompat.h" +#import "SDWebImageOperation.h" +#import "SDWebImageDefine.h" + +typedef NS_ENUM(NSInteger, SDImageCacheType) { + /** + * For query and contains op in response, means the image isn't available in the image cache + * For op in request, this type is not available and take no effect. + */ + SDImageCacheTypeNone, + /** + * For query and contains op in response, means the image was obtained from the disk cache. + * For op in request, means process only disk cache. + */ + SDImageCacheTypeDisk, + /** + * For query and contains op in response, means the image was obtained from the memory cache. + * For op in request, means process only memory cache. + */ + SDImageCacheTypeMemory, + /** + * For query and contains op in response, this type is not available and take no effect. + * For op in request, means process both memory cache and disk cache. + */ + SDImageCacheTypeAll +}; + +typedef void(^SDImageCacheQueryCompletionBlock)(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType); +typedef void(^SDImageCacheContainsCompletionBlock)(SDImageCacheType containsCacheType); + +/** + This is the image cache protocol to provide custom image cache for `SDWebImageManager`. + Though the best practice to custom image cache, is to write your own class which conform `SDMemoryCache` or `SDDiskCache` protocol for `SDImageCache` class (See more on `SDImageCacheConfig.memoryCacheClass & SDImageCacheConfig.diskCacheClass`). + However, if your own cache implementation contains more advanced feature beyond `SDImageCache` itself, you can consider to provide this instead. For example, you can even use a cache manager like `SDWebImageCachesManager` to register multiple caches. + */ +@protocol SDWebImageCache + +@required +/** + 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 completionBlock The completion block. Will not get called if the operation is cancelled + @return The operation for this query + */ +- (nullable id)queryImageForKey:(nullable NSString *)key + options:(SDWebImageOptions)options + context:(nullable SDWebImageContext *)context + 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. + + @param image The image to store + @param imageData The image data to be used for disk storage + @param key The image cache key + @param cacheType The image store op cache type + @param completionBlock A block executed after the operation is finished + */ +- (void)storeImage:(nullable UIImage *)image + imageData:(nullable NSData *)imageData + forKey:(nullable NSString *)key + cacheType:(SDImageCacheType)cacheType + completion:(nullable SDWebImageNoParamsBlock)completionBlock; + +/** + Remove the image from image cache for the given key. If cache type is memory only, completion is called synchronously, else aynchronously. + + @param key The image cache key + @param cacheType The image remove op cache type + @param completionBlock A block executed after the operation is finished + */ +- (void)removeImageForKey:(nullable NSString *)key + cacheType:(SDImageCacheType)cacheType + completion:(nullable SDWebImageNoParamsBlock)completionBlock; + +/** + Check if image cache contains the image for the given key (does not load the image). If image is cached in memory, completion is called synchronously, else aynchronously. + + @param key The image cache key + @param cacheType The image contains op cache type + @param completionBlock A block executed after the operation is finished. + */ +- (void)containsImageForKey:(nullable NSString *)key + cacheType:(SDImageCacheType)cacheType + completion:(nullable SDImageCacheContainsCompletionBlock)completionBlock; + +/** + Clear all the cached images for image cache. If cache type is memory only, completion is called synchronously, else aynchronously. + + @param cacheType The image clear op cache type + @param completionBlock A block executed after the operation is finished + */ +- (void)clearWithCacheType:(SDImageCacheType)cacheType + completion:(nullable SDWebImageNoParamsBlock)completionBlock; + +@end diff --git a/SDWebImage/SDWebImageCache.m b/SDWebImage/SDWebImageCache.m new file mode 100644 index 00000000..8203e7bd --- /dev/null +++ b/SDWebImage/SDWebImageCache.m @@ -0,0 +1,9 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDWebImageCache.h" diff --git a/SDWebImage/SDWebImageCachesManager.h b/SDWebImage/SDWebImageCachesManager.h new file mode 100644 index 00000000..c7f508dc --- /dev/null +++ b/SDWebImage/SDWebImageCachesManager.h @@ -0,0 +1,77 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import +#import "SDWebImageCache.h" + +typedef NS_ENUM(NSUInteger, SDWebImageCachesManagerOperationPolicy) { + SDWebImageCachesManagerOperationPolicySerial, // process all caches serially (from the highest priority to the lowest priority cache by order) + SDWebImageCachesManagerOperationPolicyConcurrent, // process all caches concurrently + SDWebImageCachesManagerOperationPolicyHighestOnly, // process the highest priority cache only + SDWebImageCachesManagerOperationPolicyLowestOnly // process the lowest priority cache only +}; + +@interface SDWebImageCachesManager : NSObject + +/** + Returns the global shared caches manager instance. + */ +@property (nonatomic, class, readonly, nonnull) SDWebImageCachesManager *sharedManager; + +// These are op policy for cache manager. + +/** + Operation policy for query op. + Defaults to `Serial`, means query all caches serially (one completion called then next begin) until one cache query success (`image` != nil). + */ +@property (nonatomic, assign) SDWebImageCachesManagerOperationPolicy queryOperationPolicy; + +/** + Operation policy for store op. + Defaults to `HighestOnly`, means store to the highest priority cache only. + */ +@property (nonatomic, assign) SDWebImageCachesManagerOperationPolicy storeOperationPolicy; + +/** + Operation policy for remove op. + Defaults to `Concurrent`, means remove all caches concurrently. + */ +@property (nonatomic, assign) SDWebImageCachesManagerOperationPolicy removeOperationPolicy; + +/** + Operation policy for contains op. + Defaults to `Serial`, means check all caches serially (one completion called then next begin) until one cache check success (`containsCacheType` != None). + */ +@property (nonatomic, assign) SDWebImageCachesManagerOperationPolicy containsOperationPolicy; + +/** + Operation policy for clear op. + Defaults to `Concurrent`, means clear all caches concurrently. + */ +@property (nonatomic, assign) SDWebImageCachesManagerOperationPolicy clearOperationPolicy; + +/** + All caches in caches manager. The caches array is a priority queue, which means the later added cache will have the highest priority + */ +@property (atomic, copy, readwrite, nullable) NSArray> *caches; + +/** + Add a new cache to the end of caches array. Which has the highest priority. + + @param cache cache + */ +- (void)addCache:(nonnull id)cache; + +/** + Remove a cache in the caches array. + + @param cache cache + */ +- (void)removeCache:(nonnull id)cache; + +@end diff --git a/SDWebImage/SDWebImageCachesManager.m b/SDWebImage/SDWebImageCachesManager.m new file mode 100644 index 00000000..e7644774 --- /dev/null +++ b/SDWebImage/SDWebImageCachesManager.m @@ -0,0 +1,582 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDWebImageCachesManager.h" + +// This is used for operation management, but not for operation queue execute +@interface SDWebImageCachesManagerOperation : NSOperation + +@property (nonatomic, assign, readonly) NSUInteger pendingCount; + +- (void)beginWithTotalCount:(NSUInteger)totalCount; +- (void)completeOne; +- (void)done; + +@end + +@implementation SDWebImageCachesManagerOperation + +@synthesize executing = _executing; +@synthesize finished = _finished; +@synthesize cancelled = _cancelled; + +- (void)beginWithTotalCount:(NSUInteger)totalCount { + self.executing = YES; + self.finished = NO; + _pendingCount = totalCount; +} + +- (void)completeOne { + _pendingCount = _pendingCount > 0 ? _pendingCount - 1 : 0; +} + +- (void)cancel { + self.cancelled = YES; + [self reset]; +} + +- (void)done { + self.finished = YES; + self.executing = NO; + [self reset]; +} + +- (void)reset { + _pendingCount = 0; +} + +- (void)setFinished:(BOOL)finished { + [self willChangeValueForKey:@"isFinished"]; + _finished = finished; + [self didChangeValueForKey:@"isFinished"]; +} + +- (void)setExecuting:(BOOL)executing { + [self willChangeValueForKey:@"isExecuting"]; + _executing = executing; + [self didChangeValueForKey:@"isExecuting"]; +} + +- (void)setCancelled:(BOOL)cancelled { + [self willChangeValueForKey:@"isCancelled"]; + _cancelled = cancelled; + [self didChangeValueForKey:@"isCancelled"]; +} + +@end + +@implementation SDWebImageCachesManager + ++ (SDWebImageCachesManager *)sharedManager { + static dispatch_once_t onceToken; + static SDWebImageCachesManager *manager; + dispatch_once(&onceToken, ^{ + manager = [[SDWebImageCachesManager alloc] init]; + }); + return manager; +} + +- (instancetype)init { + self = [super init]; + if (self) { + self.queryOperationPolicy = SDWebImageCachesManagerOperationPolicySerial; + self.storeOperationPolicy = SDWebImageCachesManagerOperationPolicyHighestOnly; + self.removeOperationPolicy = SDWebImageCachesManagerOperationPolicyConcurrent; + self.containsOperationPolicy = SDWebImageCachesManagerOperationPolicySerial; + self.clearOperationPolicy = SDWebImageCachesManagerOperationPolicyConcurrent; + } + return self; +} + +#pragma mark - Cache IO operations + +- (void)addCache:(id)cache { + if (![cache conformsToProtocol:@protocol(SDWebImageCache)]) { + return; + } + NSMutableArray> *mutableCaches = [self.caches mutableCopy]; + if (!mutableCaches) { + mutableCaches = [NSMutableArray array]; + } + [mutableCaches addObject:cache]; + self.caches = [mutableCaches copy]; +} + +- (void)removeCache:(id)cache { + if (![cache conformsToProtocol:@protocol(SDWebImageCache)]) { + return; + } + NSMutableArray> *mutableCaches = [self.caches mutableCopy]; + [mutableCaches removeObject:cache]; + self.caches = [mutableCaches copy]; +} + +#pragma mark - SDWebImageCache + +- (id)queryImageForKey:(NSString *)key options:(SDWebImageOptions)options context:(SDWebImageContext *)context completion:(SDImageCacheQueryCompletionBlock)completionBlock { + if (!key) { + return nil; + } + NSArray> *caches = [self.caches copy]; + NSUInteger count = caches.count; + if (count == 0) { + return nil; + } else if (count == 1) { + return [caches.firstObject queryImageForKey:key options:options context:context completion:completionBlock]; + } + switch (self.queryOperationPolicy) { + case SDWebImageCachesManagerOperationPolicyHighestOnly: { + id cache = caches.lastObject; + return [cache queryImageForKey:key options:options context:context completion:completionBlock]; + } + break; + case SDWebImageCachesManagerOperationPolicyLowestOnly: { + id cache = caches.firstObject; + return [cache queryImageForKey:key options:options context:context completion:completionBlock]; + } + break; + case SDWebImageCachesManagerOperationPolicyConcurrent: { + SDWebImageCachesManagerOperation *operation = [SDWebImageCachesManagerOperation new]; + [operation beginWithTotalCount:caches.count]; + [self concurrentQueryImageForKey:key options:options context:context completion:completionBlock enumerator:caches.reverseObjectEnumerator operation:operation]; + return operation; + } + break; + case SDWebImageCachesManagerOperationPolicySerial: { + SDWebImageCachesManagerOperation *operation = [SDWebImageCachesManagerOperation new]; + [operation beginWithTotalCount:caches.count]; + [self serialQueryImageForKey:key options:options context:context completion:completionBlock enumerator:caches.reverseObjectEnumerator operation:operation]; + return operation; + } + break; + default: + return nil; + break; + } +} + +- (void)storeImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock { + if (!key) { + return; + } + NSArray> *caches = [self.caches copy]; + NSUInteger count = caches.count; + if (count == 0) { + return; + } else if (count == 1) { + [caches.firstObject storeImage:image imageData:imageData forKey:key cacheType:cacheType completion:completionBlock]; + return; + } + switch (self.storeOperationPolicy) { + case SDWebImageCachesManagerOperationPolicyHighestOnly: { + id cache = caches.lastObject; + [cache storeImage:image imageData:imageData forKey:key cacheType:cacheType completion:completionBlock]; + } + break; + case SDWebImageCachesManagerOperationPolicyLowestOnly: { + id cache = caches.firstObject; + [cache storeImage:image imageData:imageData forKey:key cacheType:cacheType completion:completionBlock]; + } + break; + case SDWebImageCachesManagerOperationPolicyConcurrent: { + SDWebImageCachesManagerOperation *operation = [SDWebImageCachesManagerOperation new]; + [operation beginWithTotalCount:caches.count]; + [self concurrentStoreImage:image imageData:imageData forKey:key cacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator operation:operation]; + } + break; + case SDWebImageCachesManagerOperationPolicySerial: { + [self serialStoreImage:image imageData:imageData forKey:key cacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator]; + } + break; + default: + break; + } +} + +- (void)removeImageForKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock { + if (!key) { + return; + } + NSArray> *caches = [self.caches copy]; + NSUInteger count = caches.count; + if (count == 0) { + return; + } else if (count == 1) { + [caches.firstObject removeImageForKey:key cacheType:cacheType completion:completionBlock]; + return; + } + switch (self.removeOperationPolicy) { + case SDWebImageCachesManagerOperationPolicyHighestOnly: { + id cache = caches.lastObject; + [cache removeImageForKey:key cacheType:cacheType completion:completionBlock]; + } + break; + case SDWebImageCachesManagerOperationPolicyLowestOnly: { + id cache = caches.firstObject; + [cache removeImageForKey:key cacheType:cacheType completion:completionBlock]; + } + break; + case SDWebImageCachesManagerOperationPolicyConcurrent: { + SDWebImageCachesManagerOperation *operation = [SDWebImageCachesManagerOperation new]; + [operation beginWithTotalCount:caches.count]; + [self concurrentRemoveImageForKey:key cacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator operation:operation]; + } + break; + case SDWebImageCachesManagerOperationPolicySerial: { + [self serialRemoveImageForKey:key cacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator]; + } + break; + default: + break; + } +} + +- (void)containsImageForKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(SDImageCacheContainsCompletionBlock)completionBlock { + if (!key) { + return; + } + NSArray> *caches = [self.caches copy]; + NSUInteger count = caches.count; + if (count == 0) { + return; + } else if (count == 1) { + [caches.firstObject containsImageForKey:key cacheType:cacheType completion:completionBlock]; + return; + } + switch (self.clearOperationPolicy) { + case SDWebImageCachesManagerOperationPolicyHighestOnly: { + id cache = caches.lastObject; + [cache containsImageForKey:key cacheType:cacheType completion:completionBlock]; + } + break; + case SDWebImageCachesManagerOperationPolicyLowestOnly: { + id cache = caches.firstObject; + [cache containsImageForKey:key cacheType:cacheType completion:completionBlock]; + } + break; + case SDWebImageCachesManagerOperationPolicyConcurrent: { + SDWebImageCachesManagerOperation *operation = [SDWebImageCachesManagerOperation new]; + [operation beginWithTotalCount:caches.count]; + [self concurrentContainsImageForKey:key cacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator operation:operation]; + } + break; + case SDWebImageCachesManagerOperationPolicySerial: { + SDWebImageCachesManagerOperation *operation = [SDWebImageCachesManagerOperation new]; + [operation beginWithTotalCount:caches.count]; + [self serialContainsImageForKey:key cacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator operation:operation]; + } + break; + default: + break; + } +} + +- (void)clearWithCacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock { + NSArray> *caches = [self.caches copy]; + NSUInteger count = caches.count; + if (count == 0) { + return; + } else if (count == 1) { + [caches.firstObject clearWithCacheType:cacheType completion:completionBlock]; + return; + } + switch (self.clearOperationPolicy) { + case SDWebImageCachesManagerOperationPolicyHighestOnly: { + id cache = caches.lastObject; + [cache clearWithCacheType:cacheType completion:completionBlock]; + } + break; + case SDWebImageCachesManagerOperationPolicyLowestOnly: { + id cache = caches.firstObject; + [cache clearWithCacheType:cacheType completion:completionBlock]; + } + break; + case SDWebImageCachesManagerOperationPolicyConcurrent: { + SDWebImageCachesManagerOperation *operation = [SDWebImageCachesManagerOperation new]; + [operation beginWithTotalCount:caches.count]; + [self concurrentClearWithCacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator operation:operation]; + } + break; + case SDWebImageCachesManagerOperationPolicySerial: { + [self serialClearWithCacheType:cacheType completion:completionBlock enumerator:caches.reverseObjectEnumerator]; + } + break; + default: + break; + } +} + +#pragma mark - Concurrent Operation + +- (void)concurrentQueryImageForKey:(NSString *)key options:(SDWebImageOptions)options context:(SDWebImageContext *)context completion:(SDImageCacheQueryCompletionBlock)completionBlock enumerator:(NSEnumerator> *)enumerator operation:(SDWebImageCachesManagerOperation *)operation { + NSParameterAssert(enumerator); + NSParameterAssert(operation); + for (id cache in enumerator) { + [cache queryImageForKey:key options:options context:context completion:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) { + if (operation.isCancelled) { + // Cancelled + return; + } + if (operation.isFinished) { + // Finished + return; + } + [operation completeOne]; + if (image) { + // Success + [operation done]; + if (completionBlock) { + completionBlock(image, data, cacheType); + } + return; + } + if (operation.pendingCount == 0) { + // Complete + [operation done]; + if (completionBlock) { + completionBlock(nil, nil, SDImageCacheTypeNone); + } + } + }]; + } +} + +- (void)concurrentStoreImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock enumerator:(NSEnumerator> *)enumerator operation:(SDWebImageCachesManagerOperation *)operation { + NSParameterAssert(enumerator); + NSParameterAssert(operation); + for (id cache in enumerator) { + [cache storeImage:image imageData:imageData forKey:key cacheType:cacheType completion:^{ + if (operation.isCancelled) { + // Cancelled + return; + } + if (operation.isFinished) { + // Finished + return; + } + [operation completeOne]; + if (operation.pendingCount == 0) { + // Complete + [operation done]; + if (completionBlock) { + completionBlock(); + } + } + }]; + } +} + +- (void)concurrentRemoveImageForKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock enumerator:(NSEnumerator> *)enumerator operation:(SDWebImageCachesManagerOperation *)operation { + NSParameterAssert(enumerator); + NSParameterAssert(operation); + for (id cache in enumerator) { + [cache removeImageForKey:key cacheType:cacheType completion:^{ + if (operation.isCancelled) { + // Cancelled + return; + } + if (operation.isFinished) { + // Finished + return; + } + [operation completeOne]; + if (operation.pendingCount == 0) { + // Complete + [operation done]; + if (completionBlock) { + completionBlock(); + } + } + }]; + } +} + +- (void)concurrentContainsImageForKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(SDImageCacheContainsCompletionBlock)completionBlock enumerator:(NSEnumerator> *)enumerator operation:(SDWebImageCachesManagerOperation *)operation { + NSParameterAssert(enumerator); + NSParameterAssert(operation); + for (id cache in enumerator) { + [cache containsImageForKey:key cacheType:cacheType completion:^(SDImageCacheType containsCacheType) { + if (operation.isCancelled) { + // Cancelled + return; + } + if (operation.isFinished) { + // Finished + return; + } + [operation completeOne]; + if (containsCacheType != SDImageCacheTypeNone) { + // Success + [operation done]; + if (completionBlock) { + completionBlock(containsCacheType); + } + return; + } + if (operation.pendingCount == 0) { + // Complete + [operation done]; + if (completionBlock) { + completionBlock(SDImageCacheTypeNone); + } + } + }]; + } +} + +- (void)concurrentClearWithCacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock enumerator:(NSEnumerator> *)enumerator operation:(SDWebImageCachesManagerOperation *)operation { + NSParameterAssert(enumerator); + NSParameterAssert(operation); + for (id cache in enumerator) { + [cache clearWithCacheType:cacheType completion:^{ + if (operation.isCancelled) { + // Cancelled + return; + } + if (operation.isFinished) { + // Finished + return; + } + [operation completeOne]; + if (operation.pendingCount == 0) { + // Complete + [operation done]; + if (completionBlock) { + completionBlock(); + } + } + }]; + } +} + +#pragma mark - Serial Operation + +- (void)serialQueryImageForKey:(NSString *)key options:(SDWebImageOptions)options context:(SDWebImageContext *)context completion:(SDImageCacheQueryCompletionBlock)completionBlock enumerator:(NSEnumerator> *)enumerator operation:(SDWebImageCachesManagerOperation *)operation { + NSParameterAssert(enumerator); + NSParameterAssert(operation); + id cache = enumerator.nextObject; + if (!cache) { + // Complete + [operation done]; + if (completionBlock) { + completionBlock(nil, nil, SDImageCacheTypeNone); + } + return; + } + __weak typeof(self) wself = self; + [cache queryImageForKey:key options:options context:context completion:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) { + if (operation.isCancelled) { + // Cancelled + return; + } + if (operation.isFinished) { + // Finished + return; + } + [operation completeOne]; + if (image) { + // Success + [operation done]; + if (completionBlock) { + completionBlock(image, data, cacheType); + } + return; + } + // Next + [wself serialQueryImageForKey:key options:options context:context completion:completionBlock enumerator:enumerator operation:operation]; + }]; +} + +- (void)serialStoreImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock enumerator:(NSEnumerator> *)enumerator { + NSParameterAssert(enumerator); + id cache = enumerator.nextObject; + if (!cache) { + // Complete + if (completionBlock) { + completionBlock(); + } + return; + } + __weak typeof(self) wself = self; + [cache storeImage:image imageData:imageData forKey:key cacheType:cacheType completion:^{ + // Next + [wself serialStoreImage:image imageData:imageData forKey:key cacheType:cacheType completion:completionBlock enumerator:enumerator]; + }]; +} + +- (void)serialRemoveImageForKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock enumerator:(NSEnumerator> *)enumerator { + NSParameterAssert(enumerator); + id cache = enumerator.nextObject; + if (!cache) { + // Complete + if (completionBlock) { + completionBlock(); + } + return; + } + __weak typeof(self) wself = self; + [cache removeImageForKey:key cacheType:cacheType completion:^{ + // Next + [wself serialRemoveImageForKey:key cacheType:cacheType completion:completionBlock enumerator:enumerator]; + }]; +} + +- (void)serialContainsImageForKey:(NSString *)key cacheType:(SDImageCacheType)cacheType completion:(SDImageCacheContainsCompletionBlock)completionBlock enumerator:(NSEnumerator> *)enumerator operation:(SDWebImageCachesManagerOperation *)operation { + NSParameterAssert(enumerator); + NSParameterAssert(operation); + id cache = enumerator.nextObject; + if (!cache) { + // Complete + [operation done]; + if (completionBlock) { + completionBlock(SDImageCacheTypeNone); + } + return; + } + __weak typeof(self) wself = self; + [cache containsImageForKey:key cacheType:cacheType completion:^(SDImageCacheType containsCacheType) { + if (operation.isCancelled) { + // Cancelled + return; + } + if (operation.isFinished) { + // Finished + return; + } + [operation completeOne]; + if (containsCacheType != SDImageCacheTypeNone) { + // Success + [operation done]; + if (completionBlock) { + completionBlock(containsCacheType); + } + return; + } + // Next + [wself serialContainsImageForKey:key cacheType:cacheType completion:completionBlock enumerator:enumerator operation:operation]; + }]; +} + +- (void)serialClearWithCacheType:(SDImageCacheType)cacheType completion:(SDWebImageNoParamsBlock)completionBlock enumerator:(NSEnumerator> *)enumerator { + NSParameterAssert(enumerator); + id cache = enumerator.nextObject; + if (!cache) { + // Complete + if (completionBlock) { + completionBlock(); + } + return; + } + __weak typeof(self) wself = self; + [cache clearWithCacheType:cacheType completion:^{ + // Next + [wself serialClearWithCacheType:cacheType completion:completionBlock enumerator:enumerator]; + }]; +} + +@end diff --git a/SDWebImage/SDWebImageManager.h b/SDWebImage/SDWebImageManager.h index b1724058..6f338d95 100644 --- a/SDWebImage/SDWebImageManager.h +++ b/SDWebImage/SDWebImageManager.h @@ -8,8 +8,8 @@ #import "SDWebImageCompat.h" #import "SDWebImageOperation.h" +#import "SDWebImageCache.h" #import "SDWebImageDownloader.h" -#import "SDImageCache.h" #import "SDWebImageTransformer.h" #import "SDWebImageCacheKeyFilter.h" #import "SDWebImageCacheSerializer.h" @@ -99,7 +99,7 @@ SDWebImageManager *manager = [SDWebImageManager sharedManager]; /** * The image cache used by manager to query image cache. */ -@property (strong, nonatomic, readonly, nonnull) SDImageCache *imageCache; +@property (strong, nonatomic, readonly, nonnull) id imageCache; /** * The image downloader used by manager to download image. @@ -154,6 +154,18 @@ SDWebImageManager *manager = [SDWebImageManager sharedManager]; */ @property (nonatomic, assign, readonly, getter=isRunning) BOOL running; +/** + The default image cache when the manager which is created with no arguments. Such as shared manager or init. + Defaults to nil. Means using `SDImageCache.sharedImageCache` + */ +@property (nonatomic, class, nullable) id defaultImageCache; + +/** + The default image downloader for manager which is created with no arguments. Such as shared manager or init. + Defaults to nil. Means using `SDWebImageDownloader.sharedDownloader` + */ +@property (nonatomic, class, nullable) SDWebImageDownloader *defaultImageDownloader; + /** * Returns global shared manager instance. */ @@ -163,7 +175,7 @@ SDWebImageManager *manager = [SDWebImageManager sharedManager]; * Allows to specify instance of cache and image downloader used with image manager. * @return new instance of `SDWebImageManager` with specified cache and downloader. */ -- (nonnull instancetype)initWithCache:(nonnull SDImageCache *)cache downloader:(nonnull SDWebImageDownloader *)downloader NS_DESIGNATED_INITIALIZER; +- (nonnull instancetype)initWithCache:(nonnull id)cache downloader:(nonnull SDWebImageDownloader *)downloader NS_DESIGNATED_INITIALIZER; /** * Downloads the image at the given URL if not present in cache or return the cached version otherwise. @@ -213,46 +225,13 @@ SDWebImageManager *manager = [SDWebImageManager sharedManager]; progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nonnull SDInternalCompletionBlock)completedBlock; -/** - * Saves image to cache for given URL - * - * @param image The image to cache - * @param url The URL to the image - * - */ - -- (void)saveImageToCache:(nullable UIImage *)image forURL:(nullable NSURL *)url; - /** * Cancel all current operations */ - (void)cancelAll; /** - * Async check if image has already been cached - * - * @param url image url - * @param completionBlock the block to be executed when the check is finished - * - * @note the completion block is always executed on the main queue - */ -- (void)cachedImageExistsForURL:(nullable NSURL *)url - completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock; - -/** - * Async check if image has already been cached on disk only - * - * @param url image url - * @param completionBlock the block to be executed when the check is finished - * - * @note the completion block is always executed on the main queue - */ -- (void)diskImageExistsForURL:(nullable NSURL *)url - completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock; - - -/** - *Return the cache key for a given URL + * Return the cache key for a given URL */ - (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url; diff --git a/SDWebImage/SDWebImageManager.m b/SDWebImage/SDWebImageManager.m index 583e3202..e163d617 100644 --- a/SDWebImage/SDWebImageManager.m +++ b/SDWebImage/SDWebImageManager.m @@ -7,10 +7,14 @@ */ #import "SDWebImageManager.h" +#import "SDImageCache.h" #import "NSImage+Additions.h" #import "UIImage+WebCache.h" #import "SDAnimatedImage.h" +static id _defaultImageCache; +static SDWebImageDownloader *_defaultImageDownloader; + @interface SDWebImageCombinedOperation () @property (assign, nonatomic, getter = isCancelled) BOOL cancelled; @@ -31,6 +35,28 @@ @implementation SDWebImageManager ++ (id)defaultImageCache { + return _defaultImageCache; +} + ++ (void)setDefaultImageCache:(id)defaultImageCache { + if (defaultImageCache && ![defaultImageCache conformsToProtocol:@protocol(SDWebImageCache)]) { + return; + } + _defaultImageCache = defaultImageCache; +} + ++ (SDWebImageDownloader *)defaultImageDownloader { + return _defaultImageDownloader; +} + ++ (void)setDefaultImageDownloader:(SDWebImageDownloader *)defaultImageDownloader { + if (defaultImageDownloader && ![defaultImageDownloader isKindOfClass:[SDWebImageDownloader class]]) { + return; + } + _defaultImageDownloader = defaultImageDownloader; +} + + (nonnull instancetype)sharedManager { static dispatch_once_t once; static id instance; @@ -41,12 +67,18 @@ } - (nonnull instancetype)init { - SDImageCache *cache = [SDImageCache sharedImageCache]; - SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader]; + id cache = [[self class] defaultImageCache]; + if (!cache) { + cache = [SDImageCache sharedImageCache]; + } + SDWebImageDownloader *downloader = [[self class] defaultImageDownloader]; + if (!downloader) { + downloader = [SDWebImageDownloader sharedDownloader]; + } return [self initWithCache:cache downloader:downloader]; } -- (nonnull instancetype)initWithCache:(nonnull SDImageCache *)cache downloader:(nonnull SDWebImageDownloader *)downloader { +- (nonnull instancetype)initWithCache:(nonnull id)cache downloader:(nonnull SDWebImageDownloader *)downloader { if ((self = [super init])) { _imageCache = cache; _imageDownloader = downloader; @@ -76,42 +108,6 @@ return SDScaledImageForKey(key, image); } -- (void)cachedImageExistsForURL:(nullable NSURL *)url - completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock { - NSString *key = [self cacheKeyForURL:url]; - - BOOL isInMemoryCache = ([self.imageCache imageFromMemoryCacheForKey:key] != nil); - - if (isInMemoryCache) { - // making sure we call the completion block on the main queue - dispatch_async(dispatch_get_main_queue(), ^{ - if (completionBlock) { - completionBlock(YES); - } - }); - return; - } - - [self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) { - // the completion block of checkDiskCacheForImageWithKey:completion: is always called on the main queue, no need to further dispatch - if (completionBlock) { - completionBlock(isInDiskCache); - } - }]; -} - -- (void)diskImageExistsForURL:(nullable NSURL *)url - completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock { - NSString *key = [self cacheKeyForURL:url]; - - [self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) { - // the completion block of checkDiskCacheForImageWithKey:completion: is always called on the main queue, no need to further dispatch - if (completionBlock) { - completionBlock(isInDiskCache); - } - }]; -} - - (SDWebImageCombinedOperation *)loadImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDInternalCompletionBlock)completedBlock { return [self loadImageWithURL:url options:options context:nil progress:progressBlock completed:completedBlock]; } @@ -154,14 +150,6 @@ [self.runningOperations addObject:operation]; } - SDImageCacheOptions cacheOptions = 0; - if (options & SDWebImageQueryDataWhenInMemory) cacheOptions |= SDImageCacheQueryDataWhenInMemory; - if (options & SDWebImageQueryDiskSync) cacheOptions |= SDImageCacheQueryDiskSync; - if (options & SDWebImageScaleDownLargeImages) cacheOptions |= SDImageCacheScaleDownLargeImages; - if (options & SDWebImageTransformAnimatedImage) cacheOptions |= SDImageCacheTransformAnimatedImage; - if (options & SDWebImageDecodeFirstFrameOnly) cacheOptions |= SDImageCacheDecodeFirstFrameOnly; - if (options & SDWebImagePreloadAllFrames) cacheOptions |= SDImageCachePreloadAllFrames; - // Image transformer id transformer; if ([context valueForKey:SDWebImageContextCustomTransformer]) { @@ -195,7 +183,7 @@ } __weak SDWebImageCombinedOperation *weakOperation = operation; - operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key options:cacheOptions context:context done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) { + operation.cacheOperation = [self.imageCache queryImageForKey:key options:options context:context completion:^(UIImage * _Nullable cachedImage, NSData * _Nullable cachedData, SDImageCacheType cacheType) { __strong __typeof(weakOperation) strongOperation = weakOperation; if (!strongOperation || strongOperation.isCancelled) { [self safelyRemoveOperationFromRunning:strongOperation]; @@ -271,7 +259,10 @@ } } - BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly); + SDImageCacheType storeCacheType = SDImageCacheTypeAll; + if (options & SDWebImageCacheMemoryOnly) { + storeCacheType = SDImageCacheTypeMemory; + } // We've done the scale process in SDWebImageDownloader with the shared manager, this is used for custom manager and avoid extra scale. if (self != [SDWebImageManager sharedManager] && cacheKeyFilter && downloadedImage && ![downloadedImage conformsToProtocol:@protocol(SDAnimatedImage)]) { @@ -294,7 +285,7 @@ } else { cacheData = (imageWasTransformed ? nil : downloadedData); } - [self.imageCache storeImage:transformedImage imageData:cacheData forKey:cacheKey toDisk:cacheOnDisk completion:nil]; + [self.imageCache storeImage:transformedImage imageData:cacheData forKey:cacheKey cacheType:storeCacheType completion:nil]; } [self callCompletionBlockForOperation:strongSubOperation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url]; @@ -304,10 +295,10 @@ if (cacheSerializer) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ NSData *cacheData = [cacheSerializer cacheDataWithImage:downloadedImage originalData:downloadedData imageURL:url]; - [self.imageCache storeImage:downloadedImage imageData:cacheData forKey:key toDisk:cacheOnDisk completion:nil]; + [self.imageCache storeImage:downloadedImage imageData:cacheData forKey:key cacheType:storeCacheType completion:nil]; }); } else { - [self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key toDisk:cacheOnDisk completion:nil]; + [self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key cacheType:storeCacheType completion:nil]; } } [self callCompletionBlockForOperation:strongSubOperation completion:completedBlock image:downloadedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url]; @@ -331,13 +322,6 @@ return operation; } -- (void)saveImageToCache:(nullable UIImage *)image forURL:(nullable NSURL *)url { - if (image && url) { - NSString *key = [self cacheKeyForURL:url]; - [self.imageCache storeImage:image forKey:key toDisk:YES completion:nil]; - } -} - - (void)cancelAll { @synchronized (self.runningOperations) { NSArray *copiedOperations = [self.runningOperations copy]; diff --git a/WebImage/SDWebImage.h b/WebImage/SDWebImage.h index 2f57ac06..72205397 100644 --- a/WebImage/SDWebImage.h +++ b/WebImage/SDWebImage.h @@ -28,6 +28,8 @@ FOUNDATION_EXPORT const unsigned char WebImageVersionString[]; #import #import #import +#import +#import #import #import #import From 1220f735079d1fd6770c1c947519d3847f19cae9 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Fri, 13 Apr 2018 19:35:55 +0800 Subject: [PATCH 2/5] Expose the sync version of remove API --- SDWebImage/SDImageCache.h | 27 ++++++++++++++++++++++++++- SDWebImage/SDImageCache.m | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/SDWebImage/SDImageCache.h b/SDWebImage/SDImageCache.h index c22bff89..09a04710 100644 --- a/SDWebImage/SDImageCache.h +++ b/SDWebImage/SDImageCache.h @@ -167,6 +167,15 @@ typedef NSString * _Nullable (^SDImageCacheAdditionalCachePathBlock)(NSString * toDisk:(BOOL)toDisk completion:(nullable SDWebImageNoParamsBlock)completionBlock; +/** + * Synchronously store image into memory cache at the given key. + * + * @param image The image to store + * @param key The unique image cache key, usually it's image absolute URL + */ +- (void)storeImageToMemory:(nullable UIImage*)image + forKey:(nullable NSString *)key; + /** * Synchronously store image NSData into disk cache at the given key. * @@ -177,7 +186,7 @@ typedef NSString * _Nullable (^SDImageCacheAdditionalCachePathBlock)(NSString * forKey:(nullable NSString *)key; -#pragma mark - Query and Retrieve Ops +#pragma mark - Contains and Check Ops /** * Asynchronously check if image exists in disk cache already (does not load the image) @@ -195,6 +204,8 @@ typedef NSString * _Nullable (^SDImageCacheAdditionalCachePathBlock)(NSString * */ - (BOOL)diskImageDataExistsWithKey:(nullable NSString *)key; +#pragma mark - Query and Retrieve Ops + /** * Asynchronously queries the cache with operation and call the completion when done. * @@ -268,6 +279,20 @@ typedef NSString * _Nullable (^SDImageCacheAdditionalCachePathBlock)(NSString * */ - (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion; +/** + Synchronously remove the image from memory cache. + + @param key The unique image cache key + */ +- (void)removeImageFromMemoryForKey:(nullable NSString *)key; + +/** + Synchronously remove the image from disk cache. + + @param key The unique image cache key + */ +- (void)removeImageFromDiskForKey:(nullable NSString *)key; + #pragma mark - Cache clean Ops /** diff --git a/SDWebImage/SDImageCache.m b/SDWebImage/SDImageCache.m index b00746af..17d3ad8a 100644 --- a/SDWebImage/SDImageCache.m +++ b/SDWebImage/SDImageCache.m @@ -196,6 +196,14 @@ } } +- (void)storeImageToMemory:(UIImage *)image forKey:(NSString *)key { + if (!image || !key) { + return; + } + NSUInteger cost = SDMemoryCacheCostForImage(image); + [self.memCache setObject:image forKey:key cost:cost]; +} + - (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key { if (!imageData || !key) { @@ -462,6 +470,32 @@ } } +- (void)removeImageFromMemoryForKey:(NSString *)key { + if (!key) { + return; + } + + [self.memCache removeObjectForKey:key]; +} + +- (void)removeImageFromDiskForKey:(NSString *)key { + if (!key) { + return; + } + dispatch_sync(self.ioQueue, ^{ + [self _removeImageFromDiskForKey:key]; + }); +} + +// Make sure to call form io queue by caller +- (void)_removeImageFromDiskForKey:(NSString *)key { + if (!key) { + return; + } + + [self.diskCache removeDataForKey:key]; +} + #pragma mark - Cache clean Ops - (void)clearMemory { From 2c7d1a465ac8c9f4088cad892753dae59f32dc4a Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Fri, 13 Apr 2018 19:38:37 +0800 Subject: [PATCH 3/5] Update the tests for web cache protocol and caches manager --- Tests/Tests/SDAnimatedImageTest.m | 1 + Tests/Tests/SDImageCacheTests.m | 328 +++++++++++++++++++----- Tests/Tests/SDWebImageManagerTests.m | 27 +- Tests/Tests/SDWebImagePrefetcherTests.m | 1 + 4 files changed, 273 insertions(+), 84 deletions(-) diff --git a/Tests/Tests/SDAnimatedImageTest.m b/Tests/Tests/SDAnimatedImageTest.m index 0434b948..88da836c 100644 --- a/Tests/Tests/SDAnimatedImageTest.m +++ b/Tests/Tests/SDAnimatedImageTest.m @@ -8,6 +8,7 @@ */ #import "SDTestCase.h" +#import #import #import #import diff --git a/Tests/Tests/SDImageCacheTests.m b/Tests/Tests/SDImageCacheTests.m index 35191bd1..e8050382 100644 --- a/Tests/Tests/SDImageCacheTests.m +++ b/Tests/Tests/SDImageCacheTests.m @@ -9,11 +9,13 @@ #import "SDTestCase.h" #import #import +#import #import "SDWebImageTestDecoder.h" #import "SDMockFileManager.h" #import "SDWebImageTestCache.h" -NSString *kImageTestKey = @"TestImageKey.jpg"; +static NSString *kTestImageKeyJPEG = @"TestImageKey.jpg"; +static NSString *kTestImageKeyPNG = @"TestImageKey.png"; @interface SDImageCache () @@ -27,6 +29,14 @@ NSString *kImageTestKey = @"TestImageKey.jpg"; @implementation SDImageCacheTests ++ (void)setUp { + [[SDWebImageCachesManager sharedManager] addCache:[SDImageCache sharedImageCache]]; +} + ++ (void)tearDown { + [[SDWebImageCachesManager sharedManager] removeCache:[SDImageCache sharedImageCache]]; +} + - (void)test01SharedImageCache { expect([SDImageCache sharedImageCache]).toNot.beNil(); } @@ -43,14 +53,14 @@ NSString *kImageTestKey = @"TestImageKey.jpg"; - (void)test04ClearDiskCache{ XCTestExpectation *expectation = [self expectationWithDescription:@"Clear disk cache"]; - [[SDImageCache sharedImageCache] storeImage:[self imageForTesting] forKey:kImageTestKey completion:nil]; + [[SDImageCache sharedImageCache] storeImage:[self testJPEGImage] forKey:kTestImageKeyJPEG completion:nil]; [[SDImageCache sharedImageCache] clearDiskOnCompletion:^{ - expect([[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kImageTestKey]).to.equal([self imageForTesting]); - [[SDImageCache sharedImageCache] diskImageExistsWithKey:kImageTestKey completion:^(BOOL isInCache) { + expect([[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kTestImageKeyJPEG]).to.equal([self testJPEGImage]); + [[SDImageCache sharedImageCache] diskImageExistsWithKey:kTestImageKeyJPEG completion:^(BOOL isInCache) { if (!isInCache) { [[SDImageCache sharedImageCache] calculateSizeWithCompletionBlock:^(NSUInteger fileCount, NSUInteger totalSize) { expect(fileCount).to.equal(0); - [[SDImageCache sharedImageCache] removeImageForKey:kImageTestKey withCompletion:^{ + [[SDImageCache sharedImageCache] removeImageForKey:kTestImageKeyJPEG withCompletion:^{ [expectation fulfill]; }]; }]; @@ -65,12 +75,12 @@ NSString *kImageTestKey = @"TestImageKey.jpg"; - (void)test05ClearMemoryCache{ XCTestExpectation *expectation = [self expectationWithDescription:@"Clear memory cache"]; - [[SDImageCache sharedImageCache] storeImage:[self imageForTesting] forKey:kImageTestKey completion:^{ + [[SDImageCache sharedImageCache] storeImage:[self testJPEGImage] forKey:kTestImageKeyJPEG completion:^{ [[SDImageCache sharedImageCache] clearMemory]; - expect([[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kImageTestKey]).to.beNil; - [[SDImageCache sharedImageCache] diskImageExistsWithKey:kImageTestKey completion:^(BOOL isInCache) { + expect([[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kTestImageKeyJPEG]).to.beNil; + [[SDImageCache sharedImageCache] diskImageExistsWithKey:kTestImageKeyJPEG completion:^(BOOL isInCache) { if (isInCache) { - [[SDImageCache sharedImageCache] removeImageForKey:kImageTestKey withCompletion:^{ + [[SDImageCache sharedImageCache] removeImageForKey:kTestImageKeyJPEG withCompletion:^{ [expectation fulfill]; }]; } else { @@ -85,12 +95,12 @@ NSString *kImageTestKey = @"TestImageKey.jpg"; - (void)test06InsertionOfImage { XCTestExpectation *expectation = [self expectationWithDescription:@"storeImage forKey"]; - UIImage *image = [self imageForTesting]; - [[SDImageCache sharedImageCache] storeImage:image forKey:kImageTestKey completion:nil]; - expect([[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kImageTestKey]).to.equal(image); - [[SDImageCache sharedImageCache] diskImageExistsWithKey:kImageTestKey completion:^(BOOL isInCache) { + UIImage *image = [self testJPEGImage]; + [[SDImageCache sharedImageCache] storeImage:image forKey:kTestImageKeyJPEG completion:nil]; + expect([[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kTestImageKeyJPEG]).to.equal(image); + [[SDImageCache sharedImageCache] diskImageExistsWithKey:kTestImageKeyJPEG completion:^(BOOL isInCache) { if (isInCache) { - [[SDImageCache sharedImageCache] removeImageForKey:kImageTestKey withCompletion:^{ + [[SDImageCache sharedImageCache] removeImageForKey:kTestImageKeyJPEG withCompletion:^{ [expectation fulfill]; }]; } else { @@ -104,12 +114,12 @@ NSString *kImageTestKey = @"TestImageKey.jpg"; - (void)test07InsertionOfImageForcingDiskStorage { XCTestExpectation *expectation = [self expectationWithDescription:@"storeImage forKey toDisk=YES"]; - UIImage *image = [self imageForTesting]; - [[SDImageCache sharedImageCache] storeImage:image forKey:kImageTestKey toDisk:YES completion:nil]; - expect([[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kImageTestKey]).to.equal(image); - [[SDImageCache sharedImageCache] diskImageExistsWithKey:kImageTestKey completion:^(BOOL isInCache) { + UIImage *image = [self testJPEGImage]; + [[SDImageCache sharedImageCache] storeImage:image forKey:kTestImageKeyJPEG toDisk:YES completion:nil]; + expect([[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kTestImageKeyJPEG]).to.equal(image); + [[SDImageCache sharedImageCache] diskImageExistsWithKey:kTestImageKeyJPEG completion:^(BOOL isInCache) { if (isInCache) { - [[SDImageCache sharedImageCache] removeImageForKey:kImageTestKey withCompletion:^{ + [[SDImageCache sharedImageCache] removeImageForKey:kTestImageKeyJPEG withCompletion:^{ [expectation fulfill]; }]; } else { @@ -122,11 +132,11 @@ NSString *kImageTestKey = @"TestImageKey.jpg"; // Testing storeImage:forKey:toDisk:NO - (void)test08InsertionOfImageOnlyInMemory { XCTestExpectation *expectation = [self expectationWithDescription:@"storeImage forKey toDisk=NO"]; - UIImage *image = [self imageForTesting]; - [[SDImageCache sharedImageCache] storeImage:image forKey:kImageTestKey toDisk:NO completion:nil]; + UIImage *image = [self testJPEGImage]; + [[SDImageCache sharedImageCache] storeImage:image forKey:kTestImageKeyJPEG toDisk:NO completion:nil]; - expect([[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kImageTestKey]).to.equal([self imageForTesting]); - [[SDImageCache sharedImageCache] diskImageExistsWithKey:kImageTestKey completion:^(BOOL isInCache) { + expect([[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kTestImageKeyJPEG]).to.equal([self testJPEGImage]); + [[SDImageCache sharedImageCache] diskImageExistsWithKey:kTestImageKeyJPEG completion:^(BOOL isInCache) { if (!isInCache) { [expectation fulfill]; } else { @@ -134,17 +144,17 @@ NSString *kImageTestKey = @"TestImageKey.jpg"; } }]; [[SDImageCache sharedImageCache] clearMemory]; - expect([[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kImageTestKey]).to.beNil(); + expect([[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kTestImageKeyJPEG]).to.beNil(); [self waitForExpectationsWithCommonTimeout]; } - (void)test09RetrieveImageThroughNSOperation { XCTestExpectation *expectation = [self expectationWithDescription:@"queryCacheOperationForKey"]; - UIImage *imageForTesting = [self imageForTesting]; - [[SDImageCache sharedImageCache] storeImage:imageForTesting forKey:kImageTestKey completion:nil]; - NSOperation *operation = [[SDImageCache sharedImageCache] queryCacheOperationForKey:kImageTestKey done:^(UIImage *image, NSData *data, SDImageCacheType cacheType) { + UIImage *imageForTesting = [self testJPEGImage]; + [[SDImageCache sharedImageCache] storeImage:imageForTesting forKey:kTestImageKeyJPEG completion:nil]; + NSOperation *operation = [[SDImageCache sharedImageCache] queryCacheOperationForKey:kTestImageKeyJPEG done:^(UIImage *image, NSData *data, SDImageCacheType cacheType) { expect(image).to.equal(imageForTesting); - [[SDImageCache sharedImageCache] removeImageForKey:kImageTestKey withCompletion:^{ + [[SDImageCache sharedImageCache] removeImageForKey:kTestImageKeyJPEG withCompletion:^{ [expectation fulfill]; }]; }]; @@ -155,10 +165,10 @@ NSString *kImageTestKey = @"TestImageKey.jpg"; - (void)test10RemoveImageForKeyWithCompletion { XCTestExpectation *expectation = [self expectationWithDescription:@"removeImageForKey"]; - [[SDImageCache sharedImageCache] storeImage:[self imageForTesting] forKey:kImageTestKey completion:nil]; - [[SDImageCache sharedImageCache] removeImageForKey:kImageTestKey withCompletion:^{ - expect([[SDImageCache sharedImageCache] imageFromDiskCacheForKey:kImageTestKey]).to.beNil; - expect([[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kImageTestKey]).to.beNil; + [[SDImageCache sharedImageCache] storeImage:[self testJPEGImage] forKey:kTestImageKeyJPEG completion:nil]; + [[SDImageCache sharedImageCache] removeImageForKey:kTestImageKeyJPEG withCompletion:^{ + expect([[SDImageCache sharedImageCache] imageFromDiskCacheForKey:kTestImageKeyJPEG]).to.beNil; + expect([[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kTestImageKeyJPEG]).to.beNil; [expectation fulfill]; }]; [self waitForExpectationsWithCommonTimeout]; @@ -166,10 +176,10 @@ NSString *kImageTestKey = @"TestImageKey.jpg"; - (void)test11RemoveImageforKeyNotFromDiskWithCompletion{ XCTestExpectation *expectation = [self expectationWithDescription:@"removeImageForKey fromDisk:NO"]; - [[SDImageCache sharedImageCache] storeImage:[self imageForTesting] forKey:kImageTestKey completion:nil]; - [[SDImageCache sharedImageCache] removeImageForKey:kImageTestKey fromDisk:NO withCompletion:^{ - expect([[SDImageCache sharedImageCache] imageFromDiskCacheForKey:kImageTestKey]).toNot.beNil; - expect([[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kImageTestKey]).to.beNil; + [[SDImageCache sharedImageCache] storeImage:[self testJPEGImage] forKey:kTestImageKeyJPEG completion:nil]; + [[SDImageCache sharedImageCache] removeImageForKey:kTestImageKeyJPEG fromDisk:NO withCompletion:^{ + expect([[SDImageCache sharedImageCache] imageFromDiskCacheForKey:kTestImageKeyJPEG]).toNot.beNil; + expect([[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kTestImageKeyJPEG]).to.beNil; [expectation fulfill]; }]; [self waitForExpectationsWithCommonTimeout]; @@ -177,10 +187,10 @@ NSString *kImageTestKey = @"TestImageKey.jpg"; - (void)test12RemoveImageforKeyFromDiskWithCompletion{ XCTestExpectation *expectation = [self expectationWithDescription:@"removeImageForKey fromDisk:YES"]; - [[SDImageCache sharedImageCache] storeImage:[self imageForTesting] forKey:kImageTestKey completion:nil]; - [[SDImageCache sharedImageCache] removeImageForKey:kImageTestKey fromDisk:YES withCompletion:^{ - expect([[SDImageCache sharedImageCache] imageFromDiskCacheForKey:kImageTestKey]).to.beNil; - expect([[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kImageTestKey]).to.beNil; + [[SDImageCache sharedImageCache] storeImage:[self testJPEGImage] forKey:kTestImageKeyJPEG completion:nil]; + [[SDImageCache sharedImageCache] removeImageForKey:kTestImageKeyJPEG fromDisk:YES withCompletion:^{ + expect([[SDImageCache sharedImageCache] imageFromDiskCacheForKey:kTestImageKeyJPEG]).to.beNil; + expect([[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kTestImageKeyJPEG]).to.beNil; [expectation fulfill]; }]; [self waitForExpectationsWithCommonTimeout]; @@ -192,9 +202,9 @@ NSString *kImageTestKey = @"TestImageKey.jpg"; - (void)test21InitialDiskCount{ XCTestExpectation *expectation = [self expectationWithDescription:@"getDiskCount"]; - [[SDImageCache sharedImageCache] storeImage:[self imageForTesting] forKey:kImageTestKey completion:^{ + [[SDImageCache sharedImageCache] storeImage:[self testJPEGImage] forKey:kTestImageKeyJPEG completion:^{ expect([[SDImageCache sharedImageCache] getDiskCount]).to.equal(1); - [[SDImageCache sharedImageCache] removeImageForKey:kImageTestKey withCompletion:^{ + [[SDImageCache sharedImageCache] removeImageForKey:kTestImageKeyJPEG withCompletion:^{ [expectation fulfill]; }]; }]; @@ -202,7 +212,7 @@ NSString *kImageTestKey = @"TestImageKey.jpg"; } - (void)test31CachePathForAnyKey{ - NSString *path = [[SDImageCache sharedImageCache] cachePathForKey:kImageTestKey]; + NSString *path = [[SDImageCache sharedImageCache] cachePathForKey:kTestImageKeyJPEG]; expect(path).toNot.beNil; } @@ -213,10 +223,10 @@ NSString *kImageTestKey = @"TestImageKey.jpg"; - (void)test33CachePathForExistingKey{ XCTestExpectation *expectation = [self expectationWithDescription:@"cachePathForKey inPath"]; - [[SDImageCache sharedImageCache] storeImage:[self imageForTesting] forKey:kImageTestKey completion:^{ - NSString *path = [[SDImageCache sharedImageCache] cachePathForKey:kImageTestKey]; + [[SDImageCache sharedImageCache] storeImage:[self testJPEGImage] forKey:kTestImageKeyJPEG completion:^{ + NSString *path = [[SDImageCache sharedImageCache] cachePathForKey:kTestImageKeyJPEG]; expect(path).notTo.beNil; - [[SDImageCache sharedImageCache] removeImageForKey:kImageTestKey withCompletion:^{ + [[SDImageCache sharedImageCache] removeImageForKey:kTestImageKeyJPEG withCompletion:^{ [expectation fulfill]; }]; }]; @@ -240,24 +250,24 @@ NSString *kImageTestKey = @"TestImageKey.jpg"; - (void)test40InsertionOfImageData { XCTestExpectation *expectation = [self expectationWithDescription:@"Insertion of image data works"]; - UIImage *image = [[UIImage alloc] initWithContentsOfFile:[self testImagePath]]; + UIImage *image = [[UIImage alloc] initWithContentsOfFile:[self testJPEGPath]]; NSData *imageData = UIImageJPEGRepresentation(image, 1.0); - [[SDImageCache sharedImageCache] storeImageDataToDisk:imageData forKey:kImageTestKey]; + [[SDImageCache sharedImageCache] storeImageDataToDisk:imageData forKey:kTestImageKeyJPEG]; - UIImage *storedImageFromMemory = [[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kImageTestKey]; + UIImage *storedImageFromMemory = [[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kTestImageKeyJPEG]; expect(storedImageFromMemory).to.equal(nil); - NSString *cachePath = [[SDImageCache sharedImageCache] cachePathForKey:kImageTestKey]; + NSString *cachePath = [[SDImageCache sharedImageCache] cachePathForKey:kTestImageKeyJPEG]; UIImage *cachedImage = [[UIImage alloc] initWithContentsOfFile:cachePath]; NSData *storedImageData = UIImageJPEGRepresentation(cachedImage, 1.0); expect(storedImageData.length).to.beGreaterThan(0); expect(cachedImage.size).to.equal(image.size); // can't directly compare image and cachedImage because apparently there are some slight differences, even though the image is the same - [[SDImageCache sharedImageCache] diskImageExistsWithKey:kImageTestKey completion:^(BOOL isInCache) { + [[SDImageCache sharedImageCache] diskImageExistsWithKey:kTestImageKeyJPEG completion:^(BOOL isInCache) { expect(isInCache).to.equal(YES); - [[SDImageCache sharedImageCache] removeImageForKey:kImageTestKey withCompletion:^{ + [[SDImageCache sharedImageCache] removeImageForKey:kTestImageKeyJPEG withCompletion:^{ [expectation fulfill]; }]; }]; @@ -315,7 +325,7 @@ NSString *kImageTestKey = @"TestImageKey.jpg"; #endif - (void)test41StoreImageDataToDiskWithCustomFileManager { - NSData *imageData = [NSData dataWithContentsOfFile:[self testImagePath]]; + NSData *imageData = [NSData dataWithContentsOfFile:[self testJPEGPath]]; NSError *targetError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteNoPermissionError userInfo:nil]; SDMockFileManager *fileManager = [[SDMockFileManager alloc] init]; @@ -327,7 +337,7 @@ NSString *kImageTestKey = @"TestImageKey.jpg"; // This disk cache path creation will be mocked with error. SDImageCache *cache = [[SDImageCache alloc] initWithNamespace:@"test" diskCacheDirectory:@"/" config:config]; [cache storeImageDataToDisk:imageData - forKey:kImageTestKey]; + forKey:kTestImageKeyJPEG]; expect(fileManager.lastError).equal(targetError); } @@ -352,22 +362,224 @@ NSString *kImageTestKey = @"TestImageKey.jpg"; expect([diskCache isKindOfClass:[SDWebImageTestDiskCache class]]).to.beTruthy(); } +#pragma mark - SDWebImageCache & SDWebImageCachesManager +- (void)test50SDWebImageCacheQueryOp { + XCTestExpectation *expectation = [self expectationWithDescription:@"SDWebImageCache query op works"]; + [[SDImageCache sharedImageCache] storeImage:[self testJPEGImage] forKey:kTestImageKeyJPEG toDisk:NO completion:nil]; + [[SDWebImageCachesManager sharedManager] queryImageForKey:kTestImageKeyJPEG options:0 context:nil completion:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) { + expect(image).notTo.beNil(); + [expectation fulfill]; + }]; + [self waitForExpectationsWithCommonTimeout]; +} + +- (void)test51SDWebImageCacheStoreOp { + XCTestExpectation *expectation = [self expectationWithDescription:@"SDWebImageCache store op works"]; + [[SDWebImageCachesManager sharedManager] storeImage:[self testJPEGImage] imageData:nil forKey:kTestImageKeyJPEG cacheType:SDImageCacheTypeAll completion:^{ + UIImage *image = [[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kTestImageKeyJPEG]; + expect(image).notTo.beNil(); + [[SDImageCache sharedImageCache] diskImageExistsWithKey:kTestImageKeyJPEG completion:^(BOOL isInCache) { + expect(isInCache).to.beTruthy(); + [expectation fulfill]; + }]; + }]; + [self waitForExpectationsWithCommonTimeout]; +} + +- (void)test52SDWebImageCacheRemoveOp { + XCTestExpectation *expectation = [self expectationWithDescription:@"SDWebImageCache remove op works"]; + [[SDWebImageCachesManager sharedManager] removeImageForKey:kTestImageKeyJPEG cacheType:SDImageCacheTypeDisk completion:^{ + UIImage *memoryImage = [[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kTestImageKeyJPEG]; + expect(memoryImage).notTo.beNil(); + [[SDImageCache sharedImageCache] diskImageExistsWithKey:kTestImageKeyJPEG completion:^(BOOL isInCache) { + expect(isInCache).to.beFalsy(); + [expectation fulfill]; + }]; + }]; + [self waitForExpectationsWithCommonTimeout]; +} + +- (void)test53SDWebImageCacheContainsOp { + XCTestExpectation *expectation = [self expectationWithDescription:@"SDWebImageCache contains op works"]; + [[SDWebImageCachesManager sharedManager] containsImageForKey:kTestImageKeyJPEG cacheType:SDImageCacheTypeAll completion:^(SDImageCacheType containsCacheType) { + expect(containsCacheType).equal(SDImageCacheTypeMemory); + [expectation fulfill]; + }]; + [self waitForExpectationsWithCommonTimeout]; +} + +- (void)test54SDWebImageCacheClearOp { + XCTestExpectation *expectation = [self expectationWithDescription:@"SDWebImageCache clear op works"]; + [[SDWebImageCachesManager sharedManager] clearWithCacheType:SDImageCacheTypeAll completion:^{ + UIImage *memoryImage = [[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:kTestImageKeyJPEG]; + expect(memoryImage).to.beNil(); + [[SDImageCache sharedImageCache] diskImageExistsWithKey:kTestImageKeyJPEG completion:^(BOOL isInCache) { + expect(isInCache).to.beFalsy(); + [expectation fulfill]; + }]; + }]; + [self waitForExpectationsWithCommonTimeout]; +} + +- (void)test55SDWebImageCachesManagerOperationPolicySimple { + SDWebImageCachesManager *cachesManager = [[SDWebImageCachesManager alloc] init]; + SDImageCache *cache1 = [[SDImageCache alloc] initWithNamespace:@"cache1"]; + SDImageCache *cache2 = [[SDImageCache alloc] initWithNamespace:@"cache2"]; + [cachesManager addCache:cache1]; + [cachesManager addCache:cache2]; + + [[NSFileManager defaultManager] removeItemAtPath:cache1.diskCachePath error:nil]; + [[NSFileManager defaultManager] removeItemAtPath:cache2.diskCachePath error:nil]; + + // LowestOnly + cachesManager.queryOperationPolicy = SDWebImageCachesManagerOperationPolicyLowestOnly; + cachesManager.storeOperationPolicy = SDWebImageCachesManagerOperationPolicyLowestOnly; + cachesManager.removeOperationPolicy = SDWebImageCachesManagerOperationPolicyLowestOnly; + cachesManager.containsOperationPolicy = SDWebImageCachesManagerOperationPolicyLowestOnly; + cachesManager.clearOperationPolicy = SDWebImageCachesManagerOperationPolicyLowestOnly; + [cachesManager queryImageForKey:kTestImageKeyJPEG options:0 context:nil 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]; + // Check Logic works, cache1 only + UIImage *memoryImage1 = [cache1 imageFromMemoryCacheForKey:kTestImageKeyJPEG]; + expect(memoryImage1).equal([self testJPEGImage]); + [cachesManager containsImageForKey:kTestImageKeyJPEG cacheType:SDImageCacheTypeMemory completion:^(SDImageCacheType containsCacheType) { + expect(containsCacheType).equal(SDImageCacheTypeMemory); + }]; + [cachesManager removeImageForKey:kTestImageKeyJPEG cacheType:SDImageCacheTypeMemory completion:nil]; + [cachesManager clearWithCacheType:SDImageCacheTypeMemory completion:nil]; + + // HighestOnly + cachesManager.queryOperationPolicy = SDWebImageCachesManagerOperationPolicyHighestOnly; + cachesManager.storeOperationPolicy = SDWebImageCachesManagerOperationPolicyHighestOnly; + cachesManager.removeOperationPolicy = SDWebImageCachesManagerOperationPolicyHighestOnly; + cachesManager.containsOperationPolicy = SDWebImageCachesManagerOperationPolicyHighestOnly; + cachesManager.clearOperationPolicy = SDWebImageCachesManagerOperationPolicyHighestOnly; + [cachesManager queryImageForKey:kTestImageKeyPNG options:0 context:nil 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]; + // Check Logic works, cache2 only + UIImage *memoryImage2 = [cache2 imageFromMemoryCacheForKey:kTestImageKeyPNG]; + expect(memoryImage2).equal([self testPNGImage]); + [cachesManager containsImageForKey:kTestImageKeyPNG cacheType:SDImageCacheTypeMemory completion:^(SDImageCacheType containsCacheType) { + expect(containsCacheType).equal(SDImageCacheTypeMemory); + }]; + [cachesManager removeImageForKey:kTestImageKeyPNG cacheType:SDImageCacheTypeMemory completion:nil]; + [cachesManager clearWithCacheType:SDImageCacheTypeMemory completion:nil]; +} + +- (void)test56SDWebImageCachesManagerOperationPolicyConcurrent { + XCTestExpectation *expectation = [self expectationWithDescription:@"SDWebImageCachesManager operation cocurrent policy works"]; + SDWebImageCachesManager *cachesManager = [[SDWebImageCachesManager alloc] init]; + SDImageCache *cache1 = [[SDImageCache alloc] initWithNamespace:@"cache1"]; + SDImageCache *cache2 = [[SDImageCache alloc] initWithNamespace:@"cache2"]; + [cachesManager addCache:cache1]; + [cachesManager addCache:cache2]; + + [[NSFileManager defaultManager] removeItemAtPath:cache1.diskCachePath error:nil]; + [[NSFileManager defaultManager] removeItemAtPath:cache2.diskCachePath error:nil]; + + NSString *kConcurrentTestImageKey = @"kConcurrentTestImageKey"; + + // Cocurrent + // Check all concurrent op + cachesManager.queryOperationPolicy = SDWebImageCachesManagerOperationPolicyConcurrent; + cachesManager.storeOperationPolicy = SDWebImageCachesManagerOperationPolicyConcurrent; + cachesManager.removeOperationPolicy = SDWebImageCachesManagerOperationPolicyConcurrent; + cachesManager.containsOperationPolicy = SDWebImageCachesManagerOperationPolicyConcurrent; + cachesManager.clearOperationPolicy = SDWebImageCachesManagerOperationPolicyConcurrent; + [cachesManager queryImageForKey:kConcurrentTestImageKey options:0 context:nil 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]; + + // Check Logic works, check cache1(memory+JPEG) & cache2(disk+PNG) at the same time. Cache1(memory) is fast and hit. + [cache1 storeImage:[self testJPEGImage] forKey:kConcurrentTestImageKey toDisk:NO completion:nil]; + [cache2 storeImage:[self testPNGImage] forKey:kConcurrentTestImageKey toDisk:YES completion:^{ + UIImage *memoryImage1 = [cache1 imageFromMemoryCacheForKey:kConcurrentTestImageKey]; + expect(memoryImage1).notTo.beNil(); + [cache2 removeImageFromMemoryForKey:kConcurrentTestImageKey]; + [cachesManager containsImageForKey:kConcurrentTestImageKey cacheType:SDImageCacheTypeAll completion:^(SDImageCacheType containsCacheType) { + // Cache1 hit + expect(containsCacheType).equal(SDImageCacheTypeMemory); + [expectation fulfill]; + }]; + }]; + + [self waitForExpectationsWithCommonTimeout]; +} + +- (void)test57SDWebImageCachesManagerOperationPolicySerial { + XCTestExpectation *expectation = [self expectationWithDescription:@"SDWebImageCachesManager operation serial policy works"]; + SDWebImageCachesManager *cachesManager = [[SDWebImageCachesManager alloc] init]; + SDImageCache *cache1 = [[SDImageCache alloc] initWithNamespace:@"cache1"]; + SDImageCache *cache2 = [[SDImageCache alloc] initWithNamespace:@"cache2"]; + [cachesManager addCache:cache1]; + [cachesManager addCache:cache2]; + + [[NSFileManager defaultManager] removeItemAtPath:cache1.diskCachePath error:nil]; + [[NSFileManager defaultManager] removeItemAtPath:cache2.diskCachePath error:nil]; + + NSString *kSerialTestImageKey = @"kSerialTestImageKey"; + + // Serial + // Check all serial op + cachesManager.queryOperationPolicy = SDWebImageCachesManagerOperationPolicySerial; + cachesManager.storeOperationPolicy = SDWebImageCachesManagerOperationPolicySerial; + cachesManager.removeOperationPolicy = SDWebImageCachesManagerOperationPolicySerial; + cachesManager.containsOperationPolicy = SDWebImageCachesManagerOperationPolicySerial; + cachesManager.clearOperationPolicy = SDWebImageCachesManagerOperationPolicySerial; + [cachesManager queryImageForKey:kSerialTestImageKey options:0 context:nil 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]; + + // Check Logic work, from cache2(disk+PNG) -> cache1(memory+JPEG). Cache2(disk) is slow but hit. + [cache1 storeImage:[self testJPEGImage] forKey:kSerialTestImageKey toDisk:NO completion:nil]; + [cache2 storeImage:[self testPNGImage] forKey:kSerialTestImageKey toDisk:YES completion:^{ + UIImage *memoryImage1 = [cache1 imageFromMemoryCacheForKey:kSerialTestImageKey]; + expect(memoryImage1).notTo.beNil(); + [cache2 removeImageFromMemoryForKey:kSerialTestImageKey]; + [cachesManager containsImageForKey:kSerialTestImageKey cacheType:SDImageCacheTypeAll completion:^(SDImageCacheType containsCacheType) { + // Cache2 hit + expect(containsCacheType).equal(SDImageCacheTypeDisk); + [expectation fulfill]; + }]; + }]; + + [self waitForExpectationsWithCommonTimeout]; +} + #pragma mark Helper methods -- (UIImage *)imageForTesting{ +- (UIImage *)testJPEGImage { static UIImage *reusableImage = nil; if (!reusableImage) { - reusableImage = [[UIImage alloc] initWithContentsOfFile:[self testImagePath]]; + reusableImage = [[UIImage alloc] initWithContentsOfFile:[self testJPEGPath]]; } return reusableImage; } -- (NSString *)testImagePath { - +- (UIImage *)testPNGImage { + static UIImage *reusableImage = nil; + if (!reusableImage) { + reusableImage = [[UIImage alloc] initWithContentsOfFile:[self testPNGPath]]; + } + return reusableImage; +} + +- (NSString *)testJPEGPath { NSBundle *testBundle = [NSBundle bundleForClass:[self class]]; return [testBundle pathForResource:@"TestImage" ofType:@"jpg"]; } +- (NSString *)testPNGPath { + NSBundle *testBundle = [NSBundle bundleForClass:[self class]]; + return [testBundle pathForResource:@"TestImage" ofType:@"png"]; +} + - (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); return [paths[0] stringByAppendingPathComponent:fullNamespace]; diff --git a/Tests/Tests/SDWebImageManagerTests.m b/Tests/Tests/SDWebImageManagerTests.m index 8ee3a567..d9887199 100644 --- a/Tests/Tests/SDWebImageManagerTests.m +++ b/Tests/Tests/SDWebImageManagerTests.m @@ -8,6 +8,7 @@ #import "SDTestCase.h" #import +#import #import "SDWebImageTestTransformer.h" @interface SDWebImageManagerTests : SDTestCase @@ -62,32 +63,6 @@ [self waitForExpectationsWithCommonTimeout]; } -- (void)test04CachedImageExistsForURL { - __block XCTestExpectation *expectation = [self expectationWithDescription:@"Image exists in cache"]; - NSURL *imageURL = [NSURL URLWithString:kTestJpegURL]; - [[SDWebImageManager sharedManager] cachedImageExistsForURL:imageURL completion:^(BOOL isInCache) { - if (isInCache) { - [expectation fulfill]; - } else { - XCTFail(@"Image should be in cache"); - } - }]; - [self waitForExpectationsWithCommonTimeout]; -} - -- (void)test05DiskImageExistsForURL { - __block XCTestExpectation *expectation = [self expectationWithDescription:@"Image exists in disk cache"]; - NSURL *imageURL = [NSURL URLWithString:kTestJpegURL]; - [[SDWebImageManager sharedManager] diskImageExistsForURL:imageURL completion:^(BOOL isInCache) { - if (isInCache) { - [expectation fulfill]; - } else { - XCTFail(@"Image should be in cache"); - } - }]; - [self waitForExpectationsWithCommonTimeout]; -} - - (void)test06CancellAll { XCTestExpectation *expectation = [self expectationWithDescription:@"Cancel"]; diff --git a/Tests/Tests/SDWebImagePrefetcherTests.m b/Tests/Tests/SDWebImagePrefetcherTests.m index 0b62a568..c6cbb3c3 100644 --- a/Tests/Tests/SDWebImagePrefetcherTests.m +++ b/Tests/Tests/SDWebImagePrefetcherTests.m @@ -8,6 +8,7 @@ */ #import "SDTestCase.h" +#import #import @interface SDWebImagePrefetcherTests : SDTestCase From 632a2bb1100699d7371e64597f156b832075b670 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Tue, 17 Apr 2018 19:49:03 +0800 Subject: [PATCH 4/5] Rename all the image cache block naming with the unite naming (Prefix `SDImageCache`) --- SDWebImage/SDImageCache.h | 16 +++++++--------- SDWebImage/SDImageCache.m | 10 +++++----- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/SDWebImage/SDImageCache.h b/SDWebImage/SDImageCache.h index 09a04710..a1f4b971 100644 --- a/SDWebImage/SDImageCache.h +++ b/SDWebImage/SDImageCache.h @@ -46,11 +46,9 @@ typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) { SDImageCachePreloadAllFrames = 1 << 6 }; -typedef void(^SDCacheQueryCompletedBlock)(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType); +typedef void(^SDImageCacheCheckCompletionBlock)(BOOL isInCache); -typedef void(^SDWebImageCheckCacheCompletionBlock)(BOOL isInCache); - -typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger totalSize); +typedef void(^SDImageCacheCalculateSizeBlock)(NSUInteger fileCount, NSUInteger totalSize); typedef NSString * _Nullable (^SDImageCacheAdditionalCachePathBlock)(NSString * _Nonnull key); @@ -195,7 +193,7 @@ typedef NSString * _Nullable (^SDImageCacheAdditionalCachePathBlock)(NSString * * @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)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock; +- (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDImageCacheCheckCompletionBlock)completionBlock; /** * Synchronously check if image data exists in disk cache already (does not load the image) @@ -214,7 +212,7 @@ typedef NSString * _Nullable (^SDImageCacheAdditionalCachePathBlock)(NSString * * * @return a NSOperation instance containing the cache op */ -- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock; +- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDImageCacheQueryCompletionBlock)doneBlock; /** * Asynchronously queries the cache with operation and call the completion when done. @@ -225,7 +223,7 @@ typedef NSString * _Nullable (^SDImageCacheAdditionalCachePathBlock)(NSString * * * @return a NSOperation instance containing the cache op */ -- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock; +- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDImageCacheQueryCompletionBlock)doneBlock; /** * Asynchronously queries the cache with operation and call the completion when done. @@ -237,7 +235,7 @@ typedef NSString * _Nullable (^SDImageCacheAdditionalCachePathBlock)(NSString * * * @return a NSOperation instance containing the cache op */ -- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options context:(nullable SDWebImageContext *)context done:(nullable SDCacheQueryCompletedBlock)doneBlock; +- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options context:(nullable SDWebImageContext *)context done:(nullable SDImageCacheQueryCompletionBlock)doneBlock; /** * Synchronously query the memory cache. @@ -327,7 +325,7 @@ typedef NSString * _Nullable (^SDImageCacheAdditionalCachePathBlock)(NSString * /** * Asynchronously calculate the disk cache's size. */ -- (void)calculateSizeWithCompletionBlock:(nullable SDWebImageCalculateSizeBlock)completionBlock; +- (void)calculateSizeWithCompletionBlock:(nullable SDImageCacheCalculateSizeBlock)completionBlock; @end diff --git a/SDWebImage/SDImageCache.m b/SDWebImage/SDImageCache.m index 17d3ad8a..c1d8eb6b 100644 --- a/SDWebImage/SDImageCache.m +++ b/SDWebImage/SDImageCache.m @@ -226,7 +226,7 @@ #pragma mark - Query and Retrieve Ops -- (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock { +- (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDImageCacheCheckCompletionBlock)completionBlock { dispatch_async(self.ioQueue, ^{ BOOL exists = [self _diskImageDataExistsWithKey:key]; if (completionBlock) { @@ -358,15 +358,15 @@ } } -- (nullable NSOperation *)queryCacheOperationForKey:(NSString *)key done:(SDCacheQueryCompletedBlock)doneBlock { +- (nullable NSOperation *)queryCacheOperationForKey:(NSString *)key done:(SDImageCacheQueryCompletionBlock)doneBlock { return [self queryCacheOperationForKey:key options:0 done:doneBlock]; } -- (nullable NSOperation *)queryCacheOperationForKey:(NSString *)key options:(SDImageCacheOptions)options done:(SDCacheQueryCompletedBlock)doneBlock { +- (nullable NSOperation *)queryCacheOperationForKey:(NSString *)key options:(SDImageCacheOptions)options done:(SDImageCacheQueryCompletionBlock)doneBlock { return [self queryCacheOperationForKey:key options:options context:nil done:doneBlock]; } -- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options context:(nullable SDWebImageContext *)context done:(nullable SDCacheQueryCompletedBlock)doneBlock { +- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options context:(nullable SDWebImageContext *)context done:(nullable SDImageCacheQueryCompletionBlock)doneBlock { if (!key) { if (doneBlock) { doneBlock(nil, nil, SDImageCacheTypeNone); @@ -577,7 +577,7 @@ return count; } -- (void)calculateSizeWithCompletionBlock:(nullable SDWebImageCalculateSizeBlock)completionBlock { +- (void)calculateSizeWithCompletionBlock:(nullable SDImageCacheCalculateSizeBlock)completionBlock { dispatch_async(self.ioQueue, ^{ NSUInteger fileCount = [self.diskCache totalCount]; NSUInteger totalSize = [self.diskCache totalSize]; From 5a32da7953adfb6c5da58c6b1599aa5c7483c3c1 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Tue, 17 Apr 2018 20:04:59 +0800 Subject: [PATCH 5/5] Move the complicated built-in decode process into a global function to allow user who custom web cache use it --- SDWebImage/SDImageCache.m | 47 +++++++++------------------------- SDWebImage/SDWebImageCache.h | 12 +++++++++ SDWebImage/SDWebImageCache.m | 49 ++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 35 deletions(-) diff --git a/SDWebImage/SDImageCache.m b/SDWebImage/SDImageCache.m index c1d8eb6b..dd92e8e4 100644 --- a/SDWebImage/SDImageCache.m +++ b/SDWebImage/SDImageCache.m @@ -317,41 +317,7 @@ - (nullable UIImage *)diskImageForKey:(nullable NSString *)key data:(nullable NSData *)data options:(SDImageCacheOptions)options context:(SDWebImageContext *)context { if (data) { - UIImage *image; - BOOL decodeFirstFrame = options & SDImageCacheDecodeFirstFrameOnly; - NSNumber *scaleValue = [context valueForKey:SDWebImageContextImageScaleFactor]; - CGFloat scale = scaleValue.doubleValue >= 1 ? scaleValue.doubleValue : SDImageScaleFactorForKey(key); - if (!decodeFirstFrame) { - // check whether we should use `SDAnimatedImage` - if ([context valueForKey:SDWebImageContextAnimatedImageClass]) { - Class animatedImageClass = [context valueForKey:SDWebImageContextAnimatedImageClass]; - if ([animatedImageClass isSubclassOfClass:[UIImage class]] && [animatedImageClass conformsToProtocol:@protocol(SDAnimatedImage)]) { - image = [[animatedImageClass alloc] initWithData:data scale:scale]; - if (options & SDImageCachePreloadAllFrames && [image respondsToSelector:@selector(preloadAllFrames)]) { - [((id)image) preloadAllFrames]; - } - } - } - } - if (!image) { - image = [[SDWebImageCodersManager sharedManager] decodedImageWithData:data options:@{SDWebImageCoderDecodeFirstFrameOnly : @(decodeFirstFrame), SDWebImageCoderDecodeScaleFactor : @(scale)}]; - } - BOOL shouldDecode = (options & SDImageCacheAvoidDecodeImage) == 0; - if ([image conformsToProtocol:@protocol(SDAnimatedImage)]) { - // `SDAnimatedImage` do not decode - shouldDecode = NO; - } else if (image.sd_isAnimated) { - // animated image do not decode - shouldDecode = NO; - } - if (shouldDecode) { - BOOL shouldScaleDown = options & SDImageCacheScaleDownLargeImages; - if (shouldScaleDown) { - image = [SDWebImageCoderHelper decodedAndScaledDownImageWithImage:image limitBytes:0]; - } else { - image = [SDWebImageCoderHelper decodedImageWithImage:image]; - } - } + UIImage *image = SDWebImageCacheDecodeImageData(data, key, [[self class] imageOptionsFromCacheOptions:options], context); return image; } else { return nil; @@ -589,6 +555,17 @@ }); } +#pragma mark - Helper ++ (SDWebImageOptions)imageOptionsFromCacheOptions:(SDImageCacheOptions)cacheOptions { + SDWebImageOptions options = 0; + if (cacheOptions & SDImageCacheScaleDownLargeImages) options |= SDWebImageScaleDownLargeImages; + if (cacheOptions & SDImageCacheDecodeFirstFrameOnly) options |= SDWebImageDecodeFirstFrameOnly; + if (cacheOptions & SDImageCachePreloadAllFrames) options |= SDWebImagePreloadAllFrames; + if (cacheOptions & SDImageCacheAvoidDecodeImage) options |= SDWebImageAvoidDecodeImage; + + return options; +} + @end @implementation SDImageCache (SDWebImageCache) diff --git a/SDWebImage/SDWebImageCache.h b/SDWebImage/SDWebImageCache.h index ed101362..c5aeb0b4 100644 --- a/SDWebImage/SDWebImageCache.h +++ b/SDWebImage/SDWebImageCache.h @@ -37,6 +37,18 @@ typedef NS_ENUM(NSInteger, SDImageCacheType) { typedef void(^SDImageCacheQueryCompletionBlock)(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType); typedef void(^SDImageCacheContainsCompletionBlock)(SDImageCacheType containsCacheType); +/** + This is the built-in decoding process for image query from cache. + @note If you want to implement your custom loader with `queryImageForKey:options:context:completion:` API, but also want to keep compatible with SDWebImage's behavior, you'd better use this to produce image. + + @param imageData The image data from the cache. Should not be nil + @param cacheKey The image cache key from the input. Should not be nil + @param options The options arg from the input + @param context The context arg from the input + @return The decoded image for current image data query from cache + */ +FOUNDATION_EXPORT UIImage * _Nullable SDWebImageCacheDecodeImageData(NSData * _Nonnull imageData, NSString * _Nonnull cacheKey, SDWebImageOptions options, SDWebImageContext * _Nullable context); + /** This is the image cache protocol to provide custom image cache for `SDWebImageManager`. Though the best practice to custom image cache, is to write your own class which conform `SDMemoryCache` or `SDDiskCache` protocol for `SDImageCache` class (See more on `SDImageCacheConfig.memoryCacheClass & SDImageCacheConfig.diskCacheClass`). diff --git a/SDWebImage/SDWebImageCache.m b/SDWebImage/SDWebImageCache.m index 8203e7bd..bfd67063 100644 --- a/SDWebImage/SDWebImageCache.m +++ b/SDWebImage/SDWebImageCache.m @@ -7,3 +7,52 @@ */ #import "SDWebImageCache.h" +#import "SDWebImageCodersManager.h" +#import "SDWebImageCoderHelper.h" +#import "SDAnimatedImage.h" +#import "UIImage+WebCache.h" + +UIImage * _Nullable SDWebImageCacheDecodeImageData(NSData * _Nonnull imageData, NSString * _Nonnull cacheKey, SDWebImageOptions options, SDWebImageContext * _Nullable context) { + UIImage *image; + BOOL decodeFirstFrame = options & SDWebImageDecodeFirstFrameOnly; + NSNumber *scaleValue = [context valueForKey:SDWebImageContextImageScaleFactor]; + CGFloat scale = scaleValue.doubleValue >= 1 ? scaleValue.doubleValue : SDImageScaleFactorForKey(cacheKey); + if (scale < 1) { + scale = 1; + } + if (!decodeFirstFrame) { + // check whether we should use `SDAnimatedImage` + if ([context valueForKey:SDWebImageContextAnimatedImageClass]) { + Class animatedImageClass = [context valueForKey:SDWebImageContextAnimatedImageClass]; + if ([animatedImageClass isSubclassOfClass:[UIImage class]] && [animatedImageClass conformsToProtocol:@protocol(SDAnimatedImage)]) { + image = [[animatedImageClass alloc] initWithData:imageData scale:scale]; + if (options & SDWebImagePreloadAllFrames && [image respondsToSelector:@selector(preloadAllFrames)]) { + [((id)image) preloadAllFrames]; + } + } + } + } + if (!image) { + image = [[SDWebImageCodersManager sharedManager] decodedImageWithData:imageData options:@{SDWebImageCoderDecodeFirstFrameOnly : @(decodeFirstFrame), SDWebImageCoderDecodeScaleFactor : @(scale)}]; + } + if (image) { + BOOL shouldDecode = (options & SDWebImageAvoidDecodeImage) == 0; + if ([image conformsToProtocol:@protocol(SDAnimatedImage)]) { + // `SDAnimatedImage` do not decode + shouldDecode = NO; + } else if (image.sd_isAnimated) { + // animated image do not decode + shouldDecode = NO; + } + if (shouldDecode) { + BOOL shouldScaleDown = options & SDWebImageScaleDownLargeImages; + if (shouldScaleDown) { + image = [SDWebImageCoderHelper decodedAndScaledDownImageWithImage:image limitBytes:0]; + } else { + image = [SDWebImageCoderHelper decodedImageWithImage:image]; + } + } + } + + return image; +}