Merge pull request #2282 from dreampiggy/refactor_memory_cache_disk_cache

Refactor cache - Support custom memory cache & disk cache
This commit is contained in:
Bogdan Poplauschi 2018-04-17 12:14:06 +03:00
commit 1bc4662bde
14 changed files with 948 additions and 320 deletions

View File

@ -412,6 +412,30 @@
328BB6B32081FEE500760D6C /* SDWebImageCacheSerializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6A92081FEE500760D6C /* SDWebImageCacheSerializer.m */; };
328BB6B42081FEE500760D6C /* SDWebImageCacheSerializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6A92081FEE500760D6C /* SDWebImageCacheSerializer.m */; };
328BB6B52081FEE500760D6C /* SDWebImageCacheSerializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6A92081FEE500760D6C /* SDWebImageCacheSerializer.m */; };
328BB6C12082581100760D6C /* SDDiskCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 328BB6BD2082581100760D6C /* SDDiskCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
328BB6C22082581100760D6C /* SDDiskCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 328BB6BD2082581100760D6C /* SDDiskCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
328BB6C32082581100760D6C /* SDDiskCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 328BB6BD2082581100760D6C /* SDDiskCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
328BB6C42082581100760D6C /* SDDiskCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 328BB6BD2082581100760D6C /* SDDiskCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
328BB6C52082581100760D6C /* SDDiskCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 328BB6BD2082581100760D6C /* SDDiskCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
328BB6C62082581100760D6C /* SDDiskCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 328BB6BD2082581100760D6C /* SDDiskCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
328BB6C72082581100760D6C /* SDDiskCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6BE2082581100760D6C /* SDDiskCache.m */; };
328BB6C82082581100760D6C /* SDDiskCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6BE2082581100760D6C /* SDDiskCache.m */; };
328BB6C92082581100760D6C /* SDDiskCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6BE2082581100760D6C /* SDDiskCache.m */; };
328BB6CA2082581100760D6C /* SDDiskCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6BE2082581100760D6C /* SDDiskCache.m */; };
328BB6CB2082581100760D6C /* SDDiskCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6BE2082581100760D6C /* SDDiskCache.m */; };
328BB6CC2082581100760D6C /* SDDiskCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6BE2082581100760D6C /* SDDiskCache.m */; };
328BB6CD2082581100760D6C /* SDMemoryCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 328BB6BF2082581100760D6C /* SDMemoryCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
328BB6CE2082581100760D6C /* SDMemoryCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 328BB6BF2082581100760D6C /* SDMemoryCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
328BB6CF2082581100760D6C /* SDMemoryCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 328BB6BF2082581100760D6C /* SDMemoryCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
328BB6D02082581100760D6C /* SDMemoryCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 328BB6BF2082581100760D6C /* SDMemoryCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
328BB6D12082581100760D6C /* SDMemoryCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 328BB6BF2082581100760D6C /* SDMemoryCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
328BB6D22082581100760D6C /* SDMemoryCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 328BB6BF2082581100760D6C /* SDMemoryCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
328BB6D32082581100760D6C /* SDMemoryCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6C02082581100760D6C /* SDMemoryCache.m */; };
328BB6D42082581100760D6C /* SDMemoryCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6C02082581100760D6C /* SDMemoryCache.m */; };
328BB6D52082581100760D6C /* SDMemoryCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6C02082581100760D6C /* SDMemoryCache.m */; };
328BB6D62082581100760D6C /* SDMemoryCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6C02082581100760D6C /* SDMemoryCache.m */; };
328BB6D72082581100760D6C /* SDMemoryCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6C02082581100760D6C /* SDMemoryCache.m */; };
328BB6D82082581100760D6C /* SDMemoryCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6C02082581100760D6C /* SDMemoryCache.m */; };
3290FA041FA478AF0047D20C /* SDWebImageFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 3290FA021FA478AF0047D20C /* SDWebImageFrame.h */; settings = {ATTRIBUTES = (Public, ); }; };
3290FA051FA478AF0047D20C /* SDWebImageFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 3290FA021FA478AF0047D20C /* SDWebImageFrame.h */; settings = {ATTRIBUTES = (Public, ); }; };
3290FA061FA478AF0047D20C /* SDWebImageFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 3290FA021FA478AF0047D20C /* SDWebImageFrame.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -1520,6 +1544,10 @@
328BB69B2081FED200760D6C /* SDWebImageCacheKeyFilter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageCacheKeyFilter.m; sourceTree = "<group>"; };
328BB6A82081FEE500760D6C /* SDWebImageCacheSerializer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDWebImageCacheSerializer.h; sourceTree = "<group>"; };
328BB6A92081FEE500760D6C /* SDWebImageCacheSerializer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageCacheSerializer.m; sourceTree = "<group>"; };
328BB6BD2082581100760D6C /* SDDiskCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDDiskCache.h; sourceTree = "<group>"; };
328BB6BE2082581100760D6C /* SDDiskCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDDiskCache.m; sourceTree = "<group>"; };
328BB6BF2082581100760D6C /* SDMemoryCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDMemoryCache.h; sourceTree = "<group>"; };
328BB6C02082581100760D6C /* SDMemoryCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDMemoryCache.m; sourceTree = "<group>"; };
3290FA021FA478AF0047D20C /* SDWebImageFrame.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDWebImageFrame.h; sourceTree = "<group>"; };
3290FA031FA478AF0047D20C /* SDWebImageFrame.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageFrame.m; sourceTree = "<group>"; };
329A18571FFF5DFD008C9A2F /* UIImage+WebCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImage+WebCache.h"; path = "SDWebImage/UIImage+WebCache.h"; sourceTree = "<group>"; };
@ -2044,6 +2072,10 @@
53922D86148C56230056699D /* SDImageCache.m */,
43A918621D8308FE00B3925F /* SDImageCacheConfig.h */,
43A918631D8308FE00B3925F /* SDImageCacheConfig.m */,
328BB6BF2082581100760D6C /* SDMemoryCache.h */,
328BB6C02082581100760D6C /* SDMemoryCache.m */,
328BB6BD2082581100760D6C /* SDDiskCache.h */,
328BB6BE2082581100760D6C /* SDDiskCache.m */,
);
name = Cache;
sourceTree = "<group>";
@ -2295,6 +2327,7 @@
431739551CDFC8B70008FEB9 /* decode.h in Headers */,
00733A731BC4880E00A5A117 /* SDWebImage.h in Headers */,
323F8B651F38EF770092B609 /* cost_enc.h in Headers */,
328BB6C42082581100760D6C /* SDDiskCache.h in Headers */,
00733A701BC4880E00A5A117 /* UIImageView+HighlightedWebCache.h in Headers */,
323F8BDB1F38EF770092B609 /* vp8i_enc.h in Headers */,
80377C461F2F666300F89830 /* bit_reader_inl_utils.h in Headers */,
@ -2303,6 +2336,7 @@
00733A631BC4880E00A5A117 /* SDWebImageCompat.h in Headers */,
00733A661BC4880E00A5A117 /* SDWebImageDownloaderOperation.h in Headers */,
80377C5D1F2F666300F89830 /* thread_utils.h in Headers */,
328BB6D02082581100760D6C /* SDMemoryCache.h in Headers */,
321E60891F38E8C800405457 /* SDWebImageCoder.h in Headers */,
00733A721BC4880E00A5A117 /* UIView+WebCacheOperation.h in Headers */,
80377C481F2F666300F89830 /* bit_reader_utils.h in Headers */,
@ -2416,6 +2450,7 @@
4314D17D1D0E0E3B004B36C9 /* SDWebImagePrefetcher.h in Headers */,
80377C181F2F666300F89830 /* color_cache_utils.h in Headers */,
323F8B871F38EF770092B609 /* histogram_enc.h in Headers */,
328BB6CE2082581100760D6C /* SDMemoryCache.h in Headers */,
80377C1F1F2F666300F89830 /* huffman_utils.h in Headers */,
4314D17F1D0E0E3B004B36C9 /* UIButton+WebCache.h in Headers */,
32484764201775F600AF9E5A /* SDAnimatedImageView+WebCache.h in Headers */,
@ -2424,6 +2459,7 @@
4314D1851D0E0E3B004B36C9 /* SDWebImageDownloaderOperation.h in Headers */,
4314D1861D0E0E3B004B36C9 /* UIImageView+HighlightedWebCache.h in Headers */,
4314D1881D0E0E3B004B36C9 /* format_constants.h in Headers */,
328BB6C22082581100760D6C /* SDDiskCache.h in Headers */,
323F8B631F38EF770092B609 /* cost_enc.h in Headers */,
323F8BF71F38EF770092B609 /* animi.h in Headers */,
4314D18F1D0E0E3B004B36C9 /* UIView+WebCacheOperation.h in Headers */,
@ -2482,6 +2518,7 @@
325312CC200F09910046BF1E /* SDWebImageTransition.h in Headers */,
80377C6D1F2F666400F89830 /* huffman_utils.h in Headers */,
80377C731F2F666400F89830 /* random_utils.h in Headers */,
328BB6C52082581100760D6C /* SDDiskCache.h in Headers */,
431BB6EE1D06D2C1006A3455 /* NSData+ImageContentType.h in Headers */,
431BB6EF1D06D2C1006A3455 /* SDWebImagePrefetcher.h in Headers */,
80377C671F2F666400F89830 /* endian_inl_utils.h in Headers */,
@ -2494,6 +2531,7 @@
323F8B8A1F38EF770092B609 /* histogram_enc.h in Headers */,
80377E1E1F2F66A800F89830 /* lossless.h in Headers */,
321E60981F38E8ED00405457 /* SDWebImageImageIOCoder.h in Headers */,
328BB6D12082581100760D6C /* SDMemoryCache.h in Headers */,
4369C27B1D9807EC007E863A /* UIView+WebCache.h in Headers */,
80377ED11F2F66D500F89830 /* vp8_dec.h in Headers */,
324DF4B8200A14DC008A84CC /* SDWebImageDefine.h in Headers */,
@ -2568,6 +2606,7 @@
3248477A201775F600AF9E5A /* SDAnimatedImage.h in Headers */,
329A185E1FFF5DFD008C9A2F /* UIImage+WebCache.h in Headers */,
320224BB203979BA00E9F285 /* SDAnimatedImageRep.h in Headers */,
328BB6C62082581100760D6C /* SDDiskCache.h in Headers */,
80377E761F2F66A800F89830 /* yuv.h in Headers */,
80377C7A1F2F666400F89830 /* bit_reader_inl_utils.h in Headers */,
80377E631F2F66A800F89830 /* lossless.h in Headers */,
@ -2576,6 +2615,7 @@
4397D2D81D0DDD8C00BB2784 /* UIButton+WebCache.h in Headers */,
32F7C0742030114C00873181 /* SDWebImageTransformer.h in Headers */,
80377E641F2F66A800F89830 /* mips_macro.h in Headers */,
328BB6D22082581100760D6C /* SDMemoryCache.h in Headers */,
323F8BDD1F38EF770092B609 /* vp8i_enc.h in Headers */,
323F8B671F38EF770092B609 /* cost_enc.h in Headers */,
80377EE11F2F66D500F89830 /* vp8_dec.h in Headers */,
@ -2662,6 +2702,7 @@
4A2CAE331AB4BB7500B6BC39 /* UIImageView+HighlightedWebCache.h in Headers */,
431739521CDFC8B70008FEB9 /* mux.h in Headers */,
323F8B641F38EF770092B609 /* cost_enc.h in Headers */,
328BB6C32082581100760D6C /* SDDiskCache.h in Headers */,
4A2CAE1D1AB4BB6800B6BC39 /* SDWebImageDownloaderOperation.h in Headers */,
323F8BDA1F38EF770092B609 /* vp8i_enc.h in Headers */,
4317394E1CDFC8B70008FEB9 /* decode.h in Headers */,
@ -2670,6 +2711,7 @@
4A2CAE2B1AB4BB7500B6BC39 /* UIButton+WebCache.h in Headers */,
4A2CAE251AB4BB7000B6BC39 /* SDWebImagePrefetcher.h in Headers */,
80377C431F2F666300F89830 /* thread_utils.h in Headers */,
328BB6CF2082581100760D6C /* SDMemoryCache.h in Headers */,
321E60881F38E8C800405457 /* SDWebImageCoder.h in Headers */,
4A2CAE371AB4BB7500B6BC39 /* UIView+WebCacheOperation.h in Headers */,
80377C2E1F2F666300F89830 /* bit_reader_utils.h in Headers */,
@ -2729,6 +2771,7 @@
321E60A21F38E8F600405457 /* SDWebImageGIFCoder.h in Headers */,
5D5B9142188EE8DD006D06BD /* NSData+ImageContentType.h in Headers */,
80377BFE1F2F665300F89830 /* color_cache_utils.h in Headers */,
328BB6C12082581100760D6C /* SDDiskCache.h in Headers */,
431738C11CDFC2660008FEB9 /* mux.h in Headers */,
80377D0A1F2F66A100F89830 /* lossless.h in Headers */,
53761318155AD0D5005750A4 /* SDWebImageCompat.h in Headers */,
@ -2748,6 +2791,7 @@
80377D0B1F2F66A100F89830 /* mips_macro.h in Headers */,
329A18591FFF5DFD008C9A2F /* UIImage+WebCache.h in Headers */,
5376131A155AD0D5005750A4 /* SDWebImageDownloader.h in Headers */,
328BB6CD2082581100760D6C /* SDMemoryCache.h in Headers */,
4369C2771D9807EC007E863A /* UIView+WebCache.h in Headers */,
328BB6AA2081FEE500760D6C /* SDWebImageCacheSerializer.h in Headers */,
80377CEF1F2F66A100F89830 /* dsp.h in Headers */,
@ -3053,6 +3097,7 @@
32CF1C101FA496B000004BD1 /* SDWebImageCoderHelper.m in Sources */,
80377DAE1F2F66A700F89830 /* argb_sse2.c in Sources */,
323F8B7D1F38EF770092B609 /* frame_enc.c in Sources */,
328BB6D62082581100760D6C /* SDMemoryCache.m in Sources */,
80377EBB1F2F66D500F89830 /* frame_dec.c in Sources */,
32F7C0782030114C00873181 /* SDWebImageTransformer.m in Sources */,
80377DAF1F2F66A700F89830 /* argb.c in Sources */,
@ -3101,6 +3146,7 @@
80377EBF1F2F66D500F89830 /* tree_dec.c in Sources */,
80377DD21F2F66A700F89830 /* lossless_enc_sse41.c in Sources */,
80377DB31F2F66A700F89830 /* cost_sse2.c in Sources */,
328BB6CA2082581100760D6C /* SDDiskCache.m in Sources */,
32484760201775F600AF9E5A /* SDAnimatedImageView.m in Sources */,
80377DDE1F2F66A700F89830 /* rescaler_mips32.c in Sources */,
80377DCA1F2F66A700F89830 /* filters_sse2.c in Sources */,
@ -3265,6 +3311,7 @@
80377E9B1F2F66D400F89830 /* frame_dec.c in Sources */,
80377C1C1F2F666300F89830 /* huffman_encode_utils.c in Sources */,
323F8B451F38EF770092B609 /* analysis_enc.c in Sources */,
328BB6C82082581100760D6C /* SDDiskCache.m in Sources */,
80377C261F2F666300F89830 /* rescaler_utils.c in Sources */,
323F8BBB1F38EF770092B609 /* predictor_enc.c in Sources */,
325312CF200F09910046BF1E /* SDWebImageTransition.m in Sources */,
@ -3303,6 +3350,7 @@
327054DB206CD8B3006EA328 /* SDWebImageAPNGCoder.m in Sources */,
80377D5C1F2F66A700F89830 /* upsampling_sse2.c in Sources */,
323F8BC71F38EF770092B609 /* syntax_enc.c in Sources */,
328BB6D42082581100760D6C /* SDMemoryCache.m in Sources */,
80377D321F2F66A700F89830 /* dec_sse41.c in Sources */,
324DF4BB200A14DC008A84CC /* SDWebImageDefine.m in Sources */,
80377D451F2F66A700F89830 /* lossless_enc_msa.c in Sources */,
@ -3424,6 +3472,7 @@
80377C721F2F666400F89830 /* random_utils.c in Sources */,
80377ECB1F2F66D500F89830 /* frame_dec.c in Sources */,
80377C6A1F2F666400F89830 /* huffman_encode_utils.c in Sources */,
328BB6CB2082581100760D6C /* SDDiskCache.m in Sources */,
323F8B481F38EF770092B609 /* analysis_enc.c in Sources */,
80377DFE1F2F66A800F89830 /* dec_msa.c in Sources */,
325312D2200F09910046BF1E /* SDWebImageTransition.m in Sources */,
@ -3462,6 +3511,7 @@
327054DE206CD8B3006EA328 /* SDWebImageAPNGCoder.m in Sources */,
80377DFD1F2F66A800F89830 /* dec_mips32.c in Sources */,
323F8BCA1F38EF770092B609 /* syntax_enc.c in Sources */,
328BB6D72082581100760D6C /* SDMemoryCache.m in Sources */,
80377E2B1F2F66A800F89830 /* upsampling_sse2.c in Sources */,
324DF4BE200A14DC008A84CC /* SDWebImageDefine.m in Sources */,
80377E011F2F66A800F89830 /* dec_sse41.c in Sources */,
@ -3528,6 +3578,7 @@
323F8BEF1F38EF770092B609 /* webp_enc.c in Sources */,
323F8BA11F38EF770092B609 /* picture_csp_enc.c in Sources */,
323F8C1F1F38EF770092B609 /* muxread.c in Sources */,
328BB6CC2082581100760D6C /* SDDiskCache.m in Sources */,
323F8C0D1F38EF770092B609 /* muxedit.c in Sources */,
323F8B491F38EF770092B609 /* analysis_enc.c in Sources */,
80377E6E1F2F66A800F89830 /* upsampling_msa.c in Sources */,
@ -3550,6 +3601,7 @@
80377E4E1F2F66A800F89830 /* enc_sse2.c in Sources */,
80377E6C1F2F66A800F89830 /* rescaler.c in Sources */,
32484762201775F600AF9E5A /* SDAnimatedImageView.m in Sources */,
328BB6D82082581100760D6C /* SDMemoryCache.m in Sources */,
80377EE31F2F66D500F89830 /* vp8l_dec.c in Sources */,
328BB6B52081FEE500760D6C /* SDWebImageCacheSerializer.m in Sources */,
80377ED71F2F66D500F89830 /* alpha_dec.c in Sources */,
@ -3695,6 +3747,7 @@
32CF1C0F1FA496B000004BD1 /* SDWebImageCoderHelper.m in Sources */,
80377D6A1F2F66A700F89830 /* argb.c in Sources */,
323F8B7C1F38EF770092B609 /* frame_enc.c in Sources */,
328BB6D52082581100760D6C /* SDMemoryCache.m in Sources */,
80377EAB1F2F66D400F89830 /* frame_dec.c in Sources */,
32F7C0772030114C00873181 /* SDWebImageTransformer.m in Sources */,
43C8929D1D9D6DD90022038D /* anim_decode.c in Sources */,
@ -3743,6 +3796,7 @@
80377EAF1F2F66D400F89830 /* tree_dec.c in Sources */,
4A2CAE281AB4BB7500B6BC39 /* MKAnnotationView+WebCache.m in Sources */,
4A2CAE261AB4BB7000B6BC39 /* SDWebImagePrefetcher.m in Sources */,
328BB6C92082581100760D6C /* SDDiskCache.m in Sources */,
3248475F201775F600AF9E5A /* SDAnimatedImageView.m in Sources */,
80377C441F2F666300F89830 /* utils.c in Sources */,
80377D8D1F2F66A700F89830 /* lossless_enc_sse41.c in Sources */,
@ -3858,6 +3912,7 @@
32CF1C0D1FA496B000004BD1 /* SDWebImageCoderHelper.m in Sources */,
323F8B7A1F38EF770092B609 /* frame_enc.c in Sources */,
80377E8B1F2F66D000F89830 /* frame_dec.c in Sources */,
328BB6D32082581100760D6C /* SDMemoryCache.m in Sources */,
43C8929A1D9D6DD70022038D /* anim_decode.c in Sources */,
32F7C0752030114C00873181 /* SDWebImageTransformer.m in Sources */,
323F8B681F38EF770092B609 /* delta_palettization_enc.c in Sources */,
@ -3906,6 +3961,7 @@
5376130C155AD0D5005750A4 /* SDWebImageManager.m in Sources */,
5376130D155AD0D5005750A4 /* SDWebImagePrefetcher.m in Sources */,
80377C101F2F665300F89830 /* utils.c in Sources */,
328BB6C72082581100760D6C /* SDDiskCache.m in Sources */,
3248475D201775F600AF9E5A /* SDAnimatedImageView.m in Sources */,
80377D031F2F66A100F89830 /* lossless_enc_sse41.c in Sources */,
80377E8E1F2F66D000F89830 /* quant_dec.c in Sources */,

107
SDWebImage/SDDiskCache.h Normal file
View File

@ -0,0 +1,107 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDWebImageCompat.h"
@class SDImageCacheConfig;
// A protocol to allow custom disk cache used in SDImageCache.
@protocol SDDiskCache <NSObject>
// All of these method are called from the same global queue to avoid blocking on main queue and thread-safe problem. But it's also recommend to ensure thread-safe yourself using lock or other ways.
@required
/**
Create a new disk cache based on the specified path.
@param cachePath Full path of a directory in which the cache will write data.
Once initialized you should not read and write to this directory.
@param config The cache config to be used to create the cache.
@return A new cache object, or nil if an error occurs.
*/
- (nullable instancetype)initWithCachePath:(nonnull NSString *)cachePath config:(nonnull SDImageCacheConfig *)config;
/**
Returns a boolean value that indicates whether a given key is in cache.
This method may blocks the calling thread until file read finished.
@param key A string identifying the data. If nil, just return NO.
@return Whether the key is in cache.
*/
- (BOOL)containsDataForKey:(nonnull NSString *)key;
/**
Returns the data associated with a given key.
This method may blocks the calling thread until file read finished.
@param key A string identifying the data. If nil, just return nil.
@return The value associated with key, or nil if no value is associated with key.
*/
- (nullable NSData *)dataForKey:(nonnull NSString *)key;
/**
Sets the value of the specified key in the cache.
This method may blocks the calling thread until file write finished.
@param data The data to be stored in the cache.
@param key The key with which to associate the value. If nil, this method has no effect.
*/
- (void)setData:(nullable NSData *)data forKey:(nonnull NSString *)key;
/**
Removes the value of the specified key in the cache.
This method may blocks the calling thread until file delete finished.
@param key The key identifying the value to be removed. If nil, this method has no effect.
*/
- (void)removeDataForKey:(nonnull NSString *)key;
/**
Empties the cache.
This method may blocks the calling thread until file delete finished.
*/
- (void)removeAllData;
/**
Removes the expired data from the cache. You can choose the data to remove base on `ageLimit`, `countLimit` and `sizeLimit` options.
*/
- (void)removeExpiredData;
/**
The cache path for key
@param key A string identifying the value
@return The cache path for key. Or nil if the key can not associate to a path
*/
- (nullable NSString *)cachePathForKey:(nonnull NSString *)key;
/**
Returns the number of data in this cache.
This method may blocks the calling thread until file read finished.
@return The total data count.
*/
- (NSInteger)totalCount;
/**
Returns the total size (in bytes) of data in this cache.
This method may blocks the calling thread until file read finished.
@return The total data size in bytes.
*/
- (NSInteger)totalSize;
@end
// The built-in disk cache
@interface SDDiskCache : NSObject <SDDiskCache>
@property (nonatomic, strong, readonly, nonnull) SDImageCacheConfig *config;
- (nonnull instancetype)init NS_UNAVAILABLE;
@end

232
SDWebImage/SDDiskCache.m Normal file
View File

@ -0,0 +1,232 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDDiskCache.h"
#import "SDImageCacheConfig.h"
#import <CommonCrypto/CommonDigest.h>
@interface SDDiskCache ()
@property (nonatomic, copy) NSString *diskCachePath;
@property (nonatomic, strong, nonnull) NSFileManager *fileManager;
@end
@implementation SDDiskCache
- (instancetype)init {
NSAssert(NO, @"Use `initWithCachePath:` with the disk cache path");
return nil;
}
#pragma mark - SDcachePathForKeyDiskCache Protocol
- (instancetype)initWithCachePath:(NSString *)cachePath config:(nonnull SDImageCacheConfig *)config {
if (self = [super init]) {
_diskCachePath = cachePath;
_config = config;
[self commonInit];
}
return self;
}
- (void)commonInit {
if (self.config.fileManager) {
self.fileManager = self.config.fileManager;
} else {
self.fileManager = [NSFileManager new];
}
}
- (BOOL)containsDataForKey:(NSString *)key {
NSParameterAssert(key);
NSString *filePath = [self cachePathForKey:key];
BOOL exists = [self.fileManager fileExistsAtPath:filePath];
// fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name
// checking the key with and without the extension
if (!exists) {
exists = [self.fileManager fileExistsAtPath:filePath.stringByDeletingPathExtension];
}
return exists;
}
- (NSData *)dataForKey:(NSString *)key {
NSParameterAssert(key);
NSString *filePath = [self cachePathForKey:key];
NSData *data = [NSData dataWithContentsOfFile:filePath options:self.config.diskCacheReadingOptions error:nil];
if (data) {
return data;
}
// fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name
// checking the key with and without the extension
data = [NSData dataWithContentsOfFile:filePath.stringByDeletingPathExtension options:self.config.diskCacheReadingOptions error:nil];
if (data) {
return data;
}
return nil;
}
- (void)setData:(NSData *)data forKey:(NSString *)key {
NSParameterAssert(data);
NSParameterAssert(key);
if (![self.fileManager fileExistsAtPath:self.diskCachePath]) {
[self.fileManager createDirectoryAtPath:self.diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];
}
// get cache Path for image key
NSString *cachePathForKey = [self cachePathForKey:key];
// transform to NSUrl
NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey];
[data writeToURL:fileURL options:self.config.diskCacheWritingOptions error:nil];
// disable iCloud backup
if (self.config.shouldDisableiCloud) {
// ignore iCloud backup resource value error
[fileURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
}
}
- (void)removeDataForKey:(NSString *)key {
NSParameterAssert(key);
NSString *filePath = [self cachePathForKey:key];
[self.fileManager removeItemAtPath:filePath error:nil];
}
- (void)removeAllData {
[self.fileManager removeItemAtPath:self.diskCachePath error:nil];
[self.fileManager createDirectoryAtPath:self.diskCachePath
withIntermediateDirectories:YES
attributes:nil
error:NULL];
}
- (void)removeExpiredData {
NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];
NSArray<NSString *> *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey];
// This enumerator prefetches useful properties for our cache files.
NSDirectoryEnumerator *fileEnumerator = [self.fileManager enumeratorAtURL:diskCacheURL
includingPropertiesForKeys:resourceKeys
options:NSDirectoryEnumerationSkipsHiddenFiles
errorHandler:NULL];
NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.config.maxCacheAge];
NSMutableDictionary<NSURL *, NSDictionary<NSString *, id> *> *cacheFiles = [NSMutableDictionary dictionary];
NSUInteger currentCacheSize = 0;
// Enumerate all of the files in the cache directory. This loop has two purposes:
//
// 1. Removing files that are older than the expiration date.
// 2. Storing file attributes for the size-based cleanup pass.
NSMutableArray<NSURL *> *urlsToDelete = [[NSMutableArray alloc] init];
for (NSURL *fileURL in fileEnumerator) {
NSError *error;
NSDictionary<NSString *, id> *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:&error];
// Skip directories and errors.
if (error || !resourceValues || [resourceValues[NSURLIsDirectoryKey] boolValue]) {
continue;
}
// Remove files that are older than the expiration date;
NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];
if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
[urlsToDelete addObject:fileURL];
continue;
}
// Store a reference to this file and account for its total size.
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize += totalAllocatedSize.unsignedIntegerValue;
cacheFiles[fileURL] = resourceValues;
}
for (NSURL *fileURL in urlsToDelete) {
[self.fileManager removeItemAtURL:fileURL error:nil];
}
// If our remaining disk cache exceeds a configured maximum size, perform a second
// size-based cleanup pass. We delete the oldest files first.
NSUInteger maxCacheSize = self.config.maxCacheSize;
if (maxCacheSize > 0 && currentCacheSize > maxCacheSize) {
// Target half of our maximum cache size for this cleanup pass.
const NSUInteger desiredCacheSize = maxCacheSize / 2;
// Sort the remaining cache files by their last modification time (oldest first).
NSArray<NSURL *> *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent
usingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]];
}];
// Delete files until we fall below our desired cache size.
for (NSURL *fileURL in sortedFiles) {
if ([self.fileManager removeItemAtURL:fileURL error:nil]) {
NSDictionary<NSString *, id> *resourceValues = cacheFiles[fileURL];
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize -= totalAllocatedSize.unsignedIntegerValue;
if (currentCacheSize < desiredCacheSize) {
break;
}
}
}
}
}
- (nullable NSString *)cachePathForKey:(NSString *)key {
NSParameterAssert(key);
return [self cachePathForKey:key inPath:self.diskCachePath];
}
- (NSInteger)totalSize {
NSUInteger size = 0;
NSDirectoryEnumerator *fileEnumerator = [self.fileManager enumeratorAtPath:self.diskCachePath];
for (NSString *fileName in fileEnumerator) {
NSString *filePath = [self.diskCachePath stringByAppendingPathComponent:fileName];
NSDictionary<NSString *, id> *attrs = [self.fileManager attributesOfItemAtPath:filePath error:nil];
size += [attrs fileSize];
}
return size;
}
- (NSInteger)totalCount {
NSUInteger count = 0;
NSDirectoryEnumerator *fileEnumerator = [self.fileManager enumeratorAtPath:self.diskCachePath];
count = fileEnumerator.allObjects.count;
return count;
}
#pragma mark - Cache paths
- (nullable NSString *)cachePathForKey:(nullable NSString *)key inPath:(nonnull NSString *)path {
NSString *filename = SDDiskCacheFileNameForKey(key);
return [path stringByAppendingPathComponent:filename];
}
#pragma mark - Hash
static inline NSString * _Nullable SDDiskCacheFileNameForKey(NSString * _Nullable key) {
const char *str = key.UTF8String;
if (str == NULL) {
return nil;
}
unsigned char r[CC_MD5_DIGEST_LENGTH];
CC_MD5(str, (CC_LONG)strlen(str), r);
NSURL *keyURL = [NSURL URLWithString:key];
NSString *ext = keyURL ? keyURL.pathExtension : key.pathExtension;
NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%@",
r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10],
r[11], r[12], r[13], r[14], r[15], ext.length == 0 ? @"" : [NSString stringWithFormat:@".%@", ext]];
return filename;
}
@end

View File

@ -72,9 +72,10 @@ typedef NSString * _Nullable (^SDImageCacheAdditionalCachePathBlock)(NSString *
#pragma mark - Properties
/**
* Cache Config object - storing all kind of settings
* Cache Config object - storing all kind of settings.
* The property is copy so change of currrent config will not accidentally affect other cache's config.
*/
@property (nonatomic, nonnull, readonly) SDImageCacheConfig *config;
@property (nonatomic, copy, nonnull, readonly) SDImageCacheConfig *config;
/**
* The disk cache's root path
@ -116,11 +117,11 @@ typedef NSString * _Nullable (^SDImageCacheAdditionalCachePathBlock)(NSString *
*
* @param ns The namespace to use for this cache store
* @param directory Directory to cache disk images in
* @param fileManager The file manager for storing image, if nil then will be created new one
* @param config The cache config to be used to create the cache. You can provide custom memory cache or disk cache class in the cache config
*/
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
diskCacheDirectory:(nonnull NSString *)directory
fileManager:(nullable NSFileManager *)fileManager NS_DESIGNATED_INITIALIZER;
config:(nullable SDImageCacheConfig *)config NS_DESIGNATED_INITIALIZER;
#pragma mark - Cache paths

View File

@ -7,7 +7,8 @@
*/
#import "SDImageCache.h"
#import <CommonCrypto/CommonDigest.h>
#import "SDMemoryCache.h"
#import "SDDiskCache.h"
#import "NSImage+Additions.h"
#import "UIImage+WebCache.h"
#import "SDWebImageCodersManager.h"
@ -15,121 +16,14 @@
#import "SDWebImageCoderHelper.h"
#import "SDAnimatedImage.h"
#define LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
#define UNLOCK(lock) dispatch_semaphore_signal(lock);
static void * SDImageCacheContext = &SDImageCacheContext;
FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
#if SD_MAC
return image.size.height * image.size.width;
#elif SD_UIKIT || SD_WATCH
return image.size.height * image.size.width * image.scale * image.scale;
#endif
}
// A memory cache which auto purge the cache on memory warning and support weak cache.
@interface SDMemoryCache <KeyType, ObjectType> : NSCache <KeyType, ObjectType>
@end
// Private
@interface SDMemoryCache <KeyType, ObjectType> ()
@property (nonatomic, strong, nonnull) NSMapTable<KeyType, ObjectType> *weakCache; // strong-weak cache
@property (nonatomic, strong, nonnull) dispatch_semaphore_t weakCacheLock; // a lock to keep the access to `weakCache` thread-safe
@end
@implementation SDMemoryCache
// Current this seems no use on macOS (macOS use virtual memory and do not clear cache when memory warning). So we only override on iOS/tvOS platform.
// But in the future there may be more options and features for this subclass.
#if SD_UIKIT
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
}
- (instancetype)init {
self = [super init];
if (self) {
// Use a strong-weak maptable storing the secondary cache. Follow the doc that NSCache does not copy keys
// This is useful when the memory warning, the cache was purged. However, the image instance can be retained by other instance such as imageViews and alive.
// At this case, we can sync weak cache back and do not need to load from disk cache
self.weakCache = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:0];
self.weakCacheLock = dispatch_semaphore_create(1);
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didReceiveMemoryWarning:)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
}
return self;
}
- (void)didReceiveMemoryWarning:(NSNotification *)notification {
// Only remove cache, but keep weak cache
[super removeAllObjects];
}
// `setObject:forKey:` just call this with 0 cost. Override this is enough
- (void)setObject:(id)obj forKey:(id)key cost:(NSUInteger)g {
[super setObject:obj forKey:key cost:g];
if (key && obj) {
// Store weak cache
LOCK(self.weakCacheLock);
[self.weakCache setObject:obj forKey:key];
UNLOCK(self.weakCacheLock);
}
}
- (id)objectForKey:(id)key {
id obj = [super objectForKey:key];
if (key && !obj) {
// Check weak cache
LOCK(self.weakCacheLock);
obj = [self.weakCache objectForKey:key];
UNLOCK(self.weakCacheLock);
if (obj) {
// Sync cache
NSUInteger cost = 0;
if ([obj isKindOfClass:[UIImage class]]) {
cost = SDCacheCostForImage(obj);
}
[super setObject:obj forKey:key cost:cost];
}
}
return obj;
}
- (void)removeObjectForKey:(id)key {
[super removeObjectForKey:key];
if (key) {
// Remove weak cache
LOCK(self.weakCacheLock);
[self.weakCache removeObjectForKey:key];
UNLOCK(self.weakCacheLock);
}
}
- (void)removeAllObjects {
[super removeAllObjects];
// Manually remove should also remove weak cache
LOCK(self.weakCacheLock);
[self.weakCache removeAllObjects];
UNLOCK(self.weakCacheLock);
}
#endif
@end
@interface SDImageCache ()
#pragma mark - Properties
@property (strong, nonatomic, nonnull) SDMemoryCache *memCache;
@property (strong, nonatomic, nullable) dispatch_queue_t ioQueue;
@property (strong, nonatomic, nonnull) NSFileManager *fileManager;
@property (nonatomic, strong, nonnull) id<SDMemoryCache> memCache;
@property (nonatomic, strong, nonnull) id<SDDiskCache> diskCache;
@property (nonatomic, copy, readwrite, nonnull) SDImageCacheConfig *config;
@property (nonatomic, copy, readwrite, nonnull) NSString *diskCachePath;
@property (nonatomic, strong, nullable) dispatch_queue_t ioQueue;
@end
@ -158,27 +52,27 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
diskCacheDirectory:(nonnull NSString *)directory {
return [self initWithNamespace:ns diskCacheDirectory:directory fileManager: nil];
return [self initWithNamespace:ns diskCacheDirectory:directory config:SDImageCacheConfig.defaultCacheConfig];
}
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
diskCacheDirectory:(nonnull NSString *)directory
fileManager:(nullable NSFileManager *)fileManager {
config:(nullable SDImageCacheConfig *)config {
if ((self = [super init])) {
NSString *fullNamespace = [@"com.hackemist.SDWebImageCache." stringByAppendingString:ns];
// Create IO serial queue
_ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL);
_config = [[SDImageCacheConfig alloc] init];
// KVO config property which need to be passed
[_config addObserver:self forKeyPath:NSStringFromSelector(@selector(maxMemoryCost)) options:0 context:SDImageCacheContext];
[_config addObserver:self forKeyPath:NSStringFromSelector(@selector(maxMemoryCount)) options:0 context:SDImageCacheContext];
if (!config) {
config = SDImageCacheConfig.defaultCacheConfig;
}
_config = [config copy];
// Init the memory cache
_memCache = [[SDMemoryCache alloc] init];
_memCache.name = fullNamespace;
NSAssert([config.memoryCacheClass conformsToProtocol:@protocol(SDMemoryCache)], @"Custom memory cache class must conform to `SDMemoryCache` protocol");
_memCache = [[config.memoryCacheClass alloc] initWithConfig:_config];
// Init the disk cache
if (directory != nil) {
_diskCachePath = [directory stringByAppendingPathComponent:fullNamespace];
@ -186,22 +80,27 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
NSString *path = [self makeDiskCachePath:ns];
_diskCachePath = path;
}
dispatch_sync(_ioQueue, ^{
self.fileManager = fileManager ? fileManager : [NSFileManager new];
});
NSAssert([config.diskCacheClass conformsToProtocol:@protocol(SDDiskCache)], @"Custom disk cache class must conform to `SDDiskCache` protocol");
_diskCache = [[config.diskCacheClass alloc] initWithCachePath:_diskCachePath config:_config];
#if SD_UIKIT
// Subscribe to app events
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(deleteOldFiles)
selector:@selector(applicationWillTerminate:)
name:UIApplicationWillTerminateNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(backgroundDeleteOldFiles)
selector:@selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
#endif
#if SD_MAC
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillTerminate:)
name:NSApplicationWillTerminateNotification
object:nil];
#endif
}
@ -209,8 +108,6 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
}
- (void)dealloc {
[_config removeObserver:self forKeyPath:NSStringFromSelector(@selector(maxMemoryCost)) context:SDImageCacheContext];
[_config removeObserver:self forKeyPath:NSStringFromSelector(@selector(maxMemoryCount)) context:SDImageCacheContext];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@ -220,27 +117,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
if (!key) {
return nil;
}
return [self cachePathForKey:key inPath:self.diskCachePath];
}
- (nullable NSString *)cachePathForKey:(nullable NSString *)key inPath:(nonnull NSString *)path {
NSString *filename = [self cachedFileNameForKey:key];
return [path stringByAppendingPathComponent:filename];
}
- (nullable NSString *)cachedFileNameForKey:(nullable NSString *)key {
const char *str = key.UTF8String;
if (str == NULL) {
str = "";
}
unsigned char r[CC_MD5_DIGEST_LENGTH];
CC_MD5(str, (CC_LONG)strlen(str), r);
NSURL *keyURL = [NSURL URLWithString:key];
NSString *ext = keyURL ? keyURL.pathExtension : key.pathExtension;
NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%@",
r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10],
r[11], r[12], r[13], r[14], r[15], ext.length == 0 ? @"" : [NSString stringWithFormat:@".%@", ext]];
return filename;
return [self.diskCache cachePathForKey:key];
}
- (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace {
@ -276,7 +153,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
}
// if memory cache is enabled
if (self.config.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(image);
NSUInteger cost = SDMemoryCacheCostForImage(image);
[self.memCache setObject:image forKey:key cost:cost];
}
@ -326,28 +203,8 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
if (!imageData || !key) {
return;
}
if (![self.fileManager fileExistsAtPath:_diskCachePath]) {
if (![self.fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:nil]) {
return;
}
}
// get cache Path for image key
NSString *cachePathForKey = [self cachePathForKey:key];
// transform to NSUrl
NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey];
// NSFileManager's `createFileAtPath:` is used just for old code compatibility and will not trigger any delegate methods, so it's useless for custom NSFileManager at all.
// And also, NSFileManager's `createFileAtPath:` can only grab underlying POSIX errno, but NSData can grab errors defined in NSCocoaErrorDomain, which is better for user to check.
if (![imageData writeToURL:fileURL options:self.config.diskCacheWritingOptions error:nil]) {
return;
}
// disable iCloud backup
if (self.config.shouldDisableiCloud) {
// ignore iCloud backup resource value error
[fileURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
}
[self.diskCache setData:imageData forKey:key];
}
#pragma mark - Query and Retrieve Ops
@ -381,15 +238,8 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
if (!key) {
return NO;
}
BOOL exists = [self.fileManager fileExistsAtPath:[self cachePathForKey:key]];
// fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name
// checking the key with and without the extension
if (!exists) {
exists = [self.fileManager fileExistsAtPath:[self cachePathForKey:key].stringByDeletingPathExtension];
}
return exists;
return [self.diskCache containsDataForKey:key];
}
- (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key {
@ -399,7 +249,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
- (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key {
UIImage *diskImage = [self diskImageForKey:key];
if (diskImage && self.config.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(diskImage);
NSUInteger cost = SDMemoryCacheCostForImage(diskImage);
[self.memCache setObject:diskImage forKey:key cost:cost];
}
@ -419,15 +269,11 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
}
- (nullable NSData *)diskImageDataBySearchingAllPathsForKey:(nullable NSString *)key {
NSString *defaultPath = [self cachePathForKey:key];
NSData *data = [NSData dataWithContentsOfFile:defaultPath options:self.config.diskCacheReadingOptions error:nil];
if (data) {
return data;
if (!key) {
return nil;
}
// fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name
// checking the key with and without the extension
data = [NSData dataWithContentsOfFile:defaultPath.stringByDeletingPathExtension options:self.config.diskCacheReadingOptions error:nil];
NSData *data = [self.diskCache dataForKey:key];
if (data) {
return data;
}
@ -542,7 +388,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
// decode image data only if in-memory cache missed
diskImage = [self diskImageForKey:cacheKey data:diskData options:options context:context];
if (diskImage && self.config.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(diskImage);
NSUInteger cost = SDMemoryCacheCostForImage(diskImage);
[self.memCache setObject:diskImage forKey:cacheKey cost:cost];
}
}
@ -585,7 +431,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
if (fromDisk) {
dispatch_async(self.ioQueue, ^{
[self.fileManager removeItemAtPath:[self cachePathForKey:key] error:nil];
[self.diskCache removeDataForKey:key];
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
@ -593,24 +439,9 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
});
}
});
} else if (completion){
} else if (completion) {
completion();
}
}
#pragma mark - KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if (context == SDImageCacheContext) {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(maxMemoryCost))]) {
self.memCache.totalCostLimit = self.config.maxMemoryCost;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(maxMemoryCount))]) {
self.memCache.countLimit = self.config.maxMemoryCount;
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
#pragma mark - Cache clean Ops
@ -621,12 +452,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
- (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion {
dispatch_async(self.ioQueue, ^{
[self.fileManager removeItemAtPath:self.diskCachePath error:nil];
[self.fileManager createDirectoryAtPath:self.diskCachePath
withIntermediateDirectories:YES
attributes:nil
error:NULL];
[self.diskCache removeAllData];
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion();
@ -635,81 +461,9 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
});
}
- (void)deleteOldFiles {
[self deleteOldFilesWithCompletionBlock:nil];
}
- (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock {
dispatch_async(self.ioQueue, ^{
NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];
NSArray<NSString *> *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey];
// This enumerator prefetches useful properties for our cache files.
NSDirectoryEnumerator *fileEnumerator = [self.fileManager enumeratorAtURL:diskCacheURL
includingPropertiesForKeys:resourceKeys
options:NSDirectoryEnumerationSkipsHiddenFiles
errorHandler:NULL];
NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.config.maxCacheAge];
NSMutableDictionary<NSURL *, NSDictionary<NSString *, id> *> *cacheFiles = [NSMutableDictionary dictionary];
NSUInteger currentCacheSize = 0;
// Enumerate all of the files in the cache directory. This loop has two purposes:
//
// 1. Removing files that are older than the expiration date.
// 2. Storing file attributes for the size-based cleanup pass.
NSMutableArray<NSURL *> *urlsToDelete = [[NSMutableArray alloc] init];
for (NSURL *fileURL in fileEnumerator) {
NSError *error;
NSDictionary<NSString *, id> *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:&error];
// Skip directories and errors.
if (error || !resourceValues || [resourceValues[NSURLIsDirectoryKey] boolValue]) {
continue;
}
// Remove files that are older than the expiration date;
NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];
if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
[urlsToDelete addObject:fileURL];
continue;
}
// Store a reference to this file and account for its total size.
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize += totalAllocatedSize.unsignedIntegerValue;
cacheFiles[fileURL] = resourceValues;
}
for (NSURL *fileURL in urlsToDelete) {
[self.fileManager removeItemAtURL:fileURL error:nil];
}
// If our remaining disk cache exceeds a configured maximum size, perform a second
// size-based cleanup pass. We delete the oldest files first.
if (self.config.maxCacheSize > 0 && currentCacheSize > self.config.maxCacheSize) {
// Target half of our maximum cache size for this cleanup pass.
const NSUInteger desiredCacheSize = self.config.maxCacheSize / 2;
// Sort the remaining cache files by their last modification time (oldest first).
NSArray<NSURL *> *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent
usingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]];
}];
// Delete files until we fall below our desired cache size.
for (NSURL *fileURL in sortedFiles) {
if ([self.fileManager removeItemAtURL:fileURL error:nil]) {
NSDictionary<NSString *, id> *resourceValues = cacheFiles[fileURL];
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize -= totalAllocatedSize.unsignedIntegerValue;
if (currentCacheSize < desiredCacheSize) {
break;
}
}
}
}
[self.diskCache removeExpiredData];
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock();
@ -718,8 +472,21 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
});
}
#pragma mark - UIApplicationWillTerminateNotification
#if SD_UIKIT || SD_MAC
- (void)applicationWillTerminate:(NSNotification *)notification {
[self deleteOldFilesWithCompletionBlock:nil];
}
#endif
#pragma mark - UIApplicationDidEnterBackgroundNotification
#if SD_UIKIT
- (void)backgroundDeleteOldFiles {
- (void)applicationDidEnterBackground:(NSNotification *)notification {
if (!self.config.shouldRemoveExpiredDataWhenEnterBackground) {
return;
}
Class UIApplicationClass = NSClassFromString(@"UIApplication");
if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
return;
@ -745,12 +512,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
- (NSUInteger)getSize {
__block NSUInteger size = 0;
dispatch_sync(self.ioQueue, ^{
NSDirectoryEnumerator *fileEnumerator = [self.fileManager enumeratorAtPath:self.diskCachePath];
for (NSString *fileName in fileEnumerator) {
NSString *filePath = [self.diskCachePath stringByAppendingPathComponent:fileName];
NSDictionary<NSString *, id> *attrs = [self.fileManager attributesOfItemAtPath:filePath error:nil];
size += [attrs fileSize];
}
size = [self.diskCache totalSize];
});
return size;
}
@ -758,31 +520,15 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
- (NSUInteger)getDiskCount {
__block NSUInteger count = 0;
dispatch_sync(self.ioQueue, ^{
NSDirectoryEnumerator *fileEnumerator = [self.fileManager enumeratorAtPath:self.diskCachePath];
count = fileEnumerator.allObjects.count;
count = [self.diskCache totalCount];
});
return count;
}
- (void)calculateSizeWithCompletionBlock:(nullable SDWebImageCalculateSizeBlock)completionBlock {
NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];
dispatch_async(self.ioQueue, ^{
NSUInteger fileCount = 0;
NSUInteger totalSize = 0;
NSDirectoryEnumerator *fileEnumerator = [self.fileManager enumeratorAtURL:diskCacheURL
includingPropertiesForKeys:@[NSFileSize]
options:NSDirectoryEnumerationSkipsHiddenFiles
errorHandler:NULL];
for (NSURL *fileURL in fileEnumerator) {
NSNumber *fileSize;
[fileURL getResourceValue:&fileSize forKey:NSURLFileSizeKey error:NULL];
totalSize += fileSize.unsignedIntegerValue;
fileCount += 1;
}
NSUInteger fileCount = [self.diskCache totalCount];
NSUInteger totalSize = [self.diskCache totalSize];
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(fileCount, totalSize);

View File

@ -9,7 +9,15 @@
#import <Foundation/Foundation.h>
#import "SDWebImageCompat.h"
@interface SDImageCacheConfig : NSObject
// This class conform to NSCopying, make sure to add the property in `copyWithZone:` as well.
@interface SDImageCacheConfig : NSObject <NSCopying>
/**
Gets/Sets the default cache config used for shared instance or initialization when it does not provide any cache config. Such as `SDImageCache.sharedImageCache`.
@note You can modify the property on default cache config, which can be used for later created cache instance. The already created cache instance does not get affected.
@note You should not pass nil to this value.
*/
@property (nonatomic, class, nonnull) SDImageCacheConfig *defaultCacheConfig;
/**
* Whether or not to disable iCloud backup
@ -23,6 +31,12 @@
*/
@property (assign, nonatomic) BOOL shouldCacheImagesInMemory;
/**
* Whether or not to remove the expired disk data when application entering the background. (Not works for macOS)
* Defatuls to YES.
*/
@property (assign, nonatomic) BOOL shouldRemoveExpiredDataWhenEnterBackground;
/**
* The reading options while reading cache from disk.
* Defaults to 0. You can set this to `NSDataReadingMappedIfSafe` to improve performance.
@ -39,7 +53,7 @@
* The maximum length of time to keep an image in the cache, in seconds.
* Defaults to 1 weak.
*/
@property (assign, nonatomic) NSInteger maxCacheAge;
@property (assign, nonatomic) NSTimeInterval maxCacheAge;
/**
* The maximum size of the cache, in bytes.
@ -59,4 +73,26 @@
*/
@property (assign, nonatomic) NSUInteger maxMemoryCount;
/**
* The custom file manager for disk cache. Pass nil to let disk cache choose the proper file manager.
* Defaults to nil.
* @note This value does not support dynamic changes. Which means further modification on this value after cache initlized has no effect.
* @note Since `NSFileManager` does not support `NSCopying`. We just pass this by reference during copying. So it's not recommend to set this value on `defaultCacheConfig`.
*/
@property (strong, nonatomic, nullable) NSFileManager *fileManager;
/**
* The custom memory cache class. Provided class instance must conform to `SDMemoryCache` protocol to allow usage.
* Defaults to built-in `SDMemoryCache` class.
* @note This value does not support dynamic changes. Which means further modification on this value after cache initlized has no effect.
*/
@property (assign, nonatomic, nonnull) Class memoryCacheClass;
/**
* The custom disk cache class. Provided class instance must conform to `SDDiskCache` protocol to allow usage.
* Defaults to built-in `SDDiskCache` class.
* @note This value does not support dynamic changes. Which means further modification on this value after cache initlized has no effect.
*/
@property (assign ,nonatomic, nonnull) Class diskCacheClass;
@end

View File

@ -7,21 +7,59 @@
*/
#import "SDImageCacheConfig.h"
#import "SDMemoryCache.h"
#import "SDDiskCache.h"
static SDImageCacheConfig *_defaultCacheConfig;
static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
@implementation SDImageCacheConfig
+ (SDImageCacheConfig *)defaultCacheConfig {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_defaultCacheConfig = [SDImageCacheConfig new];
});
return _defaultCacheConfig;
}
+ (void)setDefaultCacheConfig:(SDImageCacheConfig *)defaultCacheConfig {
if (defaultCacheConfig) {
_defaultCacheConfig = defaultCacheConfig;
}
}
- (instancetype)init {
if (self = [super init]) {
_shouldDisableiCloud = YES;
_shouldCacheImagesInMemory = YES;
_shouldRemoveExpiredDataWhenEnterBackground = YES;
_diskCacheReadingOptions = 0;
_diskCacheWritingOptions = NSDataWritingAtomic;
_maxCacheAge = kDefaultCacheMaxCacheAge;
_maxCacheSize = 0;
_memoryCacheClass = [SDMemoryCache class];
_diskCacheClass = [SDDiskCache class];
}
return self;
}
- (id)copyWithZone:(NSZone *)zone {
SDImageCacheConfig *config = [[[self class] allocWithZone:zone] init];
config.shouldDisableiCloud = self.shouldDisableiCloud;
config.shouldCacheImagesInMemory = self.shouldCacheImagesInMemory;
config.shouldRemoveExpiredDataWhenEnterBackground = self.shouldRemoveExpiredDataWhenEnterBackground;
config.diskCacheReadingOptions = self.diskCacheReadingOptions;
config.diskCacheWritingOptions = self.diskCacheWritingOptions;
config.maxCacheAge = self.maxCacheAge;
config.maxCacheSize = self.maxCacheSize;
config.maxMemoryCost = self.maxMemoryCost;
config.maxMemoryCount = self.maxMemoryCount;
config.fileManager = self.fileManager; // NSFileManager does not conform to NSCopying, just pass the reference
config.memoryCacheClass = self.memoryCacheClass;
config.diskCacheClass = self.diskCacheClass;
return config;
}
@end

View File

@ -0,0 +1,81 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDWebImageCompat.h"
/**
Return the memory cache cost for specify image
@param image The image to store in cache
@return The memory cost for the image
*/
FOUNDATION_EXPORT NSUInteger SDMemoryCacheCostForImage(UIImage * _Nullable image);
@class SDImageCacheConfig;
// A protocol to allow custom memory cache used in SDImageCache.
@protocol SDMemoryCache <NSObject>
@required
/**
Create a new memory cache instance with the specify cache config. You can check `maxMemoryCost` and `maxMemoryCount` used for memory cache.
@param config The cache config to be used to create the cache.
@return The new memory cache instance.
*/
- (nonnull instancetype)initWithConfig:(nonnull SDImageCacheConfig *)config;
/**
Returns the value associated with a given key.
@param key An object identifying the value. If nil, just return nil.
@return The value associated with key, or nil if no value is associated with key.
*/
- (nullable id)objectForKey:(nonnull id)key;
/**
Sets the value of the specified key in the cache (0 cost).
@param object The object to be stored in the cache. If nil, it calls `removeObjectForKey:`.
@param key The key with which to associate the value. If nil, this method has no effect.
@discussion Unlike an NSMutableDictionary object, a cache does not copy the key
objects that are put into it.
*/
- (void)setObject:(nullable id)object forKey:(nonnull id)key;
/**
Sets the value of the specified key in the cache, and associates the key-value
pair with the specified cost.
@param object The object to store in the cache. If nil, it calls `removeObjectForKey`.
@param key The key with which to associate the value. If nil, this method has no effect.
@param cost The cost with which to associate the key-value pair.
@discussion Unlike an NSMutableDictionary object, a cache does not copy the key
objects that are put into it.
*/
- (void)setObject:(nullable id)object forKey:(nonnull id)key cost:(NSUInteger)cost;
/**
Removes the value of the specified key in the cache.
@param key The key identifying the value to be removed. If nil, this method has no effect.
*/
- (void)removeObjectForKey:(nonnull id)key;
/**
Empties the cache immediately.
*/
- (void)removeAllObjects;
@end
// A memory cache which auto purge the cache on memory warning and support weak cache.
@interface SDMemoryCache <KeyType, ObjectType> : NSCache <KeyType, ObjectType> <SDMemoryCache>
@property (nonatomic, strong, nonnull, readonly) SDImageCacheConfig *config;
@end

150
SDWebImage/SDMemoryCache.m Normal file
View File

@ -0,0 +1,150 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDMemoryCache.h"
#import "SDImageCacheConfig.h"
#define LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
#define UNLOCK(lock) dispatch_semaphore_signal(lock);
NSUInteger SDMemoryCacheCostForImage(UIImage * _Nullable image) {
#if SD_MAC
return image.size.height * image.size.width;
#elif SD_UIKIT || SD_WATCH
return image.size.height * image.size.width * image.scale * image.scale;
#endif
}
static void * SDMemoryCacheContext = &SDMemoryCacheContext;
@interface SDMemoryCache <KeyType, ObjectType> ()
@property (nonatomic, strong, nullable) SDImageCacheConfig *config;
@property (nonatomic, strong, nonnull) NSMapTable<KeyType, ObjectType> *weakCache; // strong-weak cache
@property (nonatomic, strong, nonnull) dispatch_semaphore_t weakCacheLock; // a lock to keep the access to `weakCache` thread-safe
@end
@implementation SDMemoryCache
- (void)dealloc {
[_config removeObserver:self forKeyPath:NSStringFromSelector(@selector(maxMemoryCost)) context:SDMemoryCacheContext];
[_config removeObserver:self forKeyPath:NSStringFromSelector(@selector(maxMemoryCount)) context:SDMemoryCacheContext];
#if SD_UIKIT
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
#endif
}
- (instancetype)init {
self = [super init];
if (self) {
_config = [[SDImageCacheConfig alloc] init];
[self commonInit];
}
return self;
}
- (instancetype)initWithConfig:(SDImageCacheConfig *)config {
self = [super init];
if (self) {
_config = config;
[self commonInit];
}
return self;
}
- (void)commonInit {
self.weakCache = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:0];
self.weakCacheLock = dispatch_semaphore_create(1);
SDImageCacheConfig *config = self.config;
self.totalCostLimit = config.maxMemoryCost;
self.countLimit = config.maxMemoryCount;
[config addObserver:self forKeyPath:NSStringFromSelector(@selector(maxMemoryCost)) options:0 context:SDMemoryCacheContext];
[config addObserver:self forKeyPath:NSStringFromSelector(@selector(maxMemoryCount)) options:0 context:SDMemoryCacheContext];
#if SD_UIKIT
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didReceiveMemoryWarning:)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
#endif
}
// Current this seems no use on macOS (macOS use virtual memory and do not clear cache when memory warning). So we only override on iOS/tvOS platform.
#if SD_UIKIT
- (void)didReceiveMemoryWarning:(NSNotification *)notification {
// Only remove cache, but keep weak cache
[super removeAllObjects];
}
// `setObject:forKey:` just call this with 0 cost. Override this is enough
- (void)setObject:(id)obj forKey:(id)key cost:(NSUInteger)g {
[super setObject:obj forKey:key cost:g];
if (key && obj) {
// Store weak cache
LOCK(self.weakCacheLock);
[self.weakCache setObject:obj forKey:key];
UNLOCK(self.weakCacheLock);
}
}
- (id)objectForKey:(id)key {
id obj = [super objectForKey:key];
if (key && !obj) {
// Check weak cache
LOCK(self.weakCacheLock);
obj = [self.weakCache objectForKey:key];
UNLOCK(self.weakCacheLock);
if (obj) {
// Sync cache
NSUInteger cost = 0;
if ([obj isKindOfClass:[UIImage class]]) {
cost = SDMemoryCacheCostForImage(obj);
}
[super setObject:obj forKey:key cost:cost];
}
}
return obj;
}
- (void)removeObjectForKey:(id)key {
[super removeObjectForKey:key];
if (key) {
// Remove weak cache
LOCK(self.weakCacheLock);
[self.weakCache removeObjectForKey:key];
UNLOCK(self.weakCacheLock);
}
}
- (void)removeAllObjects {
[super removeAllObjects];
// Manually remove should also remove weak cache
LOCK(self.weakCacheLock);
[self.weakCache removeAllObjects];
UNLOCK(self.weakCacheLock);
}
#endif
#pragma mark - KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if (context == SDMemoryCacheContext) {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(maxMemoryCost))]) {
self.totalCostLimit = self.config.maxMemoryCost;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(maxMemoryCount))]) {
self.countLimit = self.config.maxMemoryCount;
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
@end

View File

@ -21,6 +21,8 @@
3264FF30205D42CB00F6BD48 /* SDWebImageTestTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3264FF2E205D42CB00F6BD48 /* SDWebImageTestTransformer.m */; };
327054E2206CEFF3006EA328 /* TestImageAnimated.apng in Resources */ = {isa = PBXBuildFile; fileRef = 327054E1206CEFF3006EA328 /* TestImageAnimated.apng */; };
327054E3206CEFF3006EA328 /* TestImageAnimated.apng in Resources */ = {isa = PBXBuildFile; fileRef = 327054E1206CEFF3006EA328 /* TestImageAnimated.apng */; };
328BB6DD20825E9800760D6C /* SDWebImageTestCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6DC20825E9800760D6C /* SDWebImageTestCache.m */; };
328BB6DE20825E9800760D6C /* SDWebImageTestCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6DC20825E9800760D6C /* SDWebImageTestCache.m */; };
32A571562037DB2D002EDAAE /* SDAnimatedImageTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A571552037DB2D002EDAAE /* SDAnimatedImageTest.m */; };
32B99E8B203AF8690017FD66 /* SDCategoriesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32B99E8A203AF8690017FD66 /* SDCategoriesTests.m */; };
32B99E9B203B2EDD0017FD66 /* SDTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D7AF05F1F329763000083C2 /* SDTestCase.m */; };
@ -72,6 +74,8 @@
3264FF2D205D42CB00F6BD48 /* SDWebImageTestTransformer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDWebImageTestTransformer.h; sourceTree = "<group>"; };
3264FF2E205D42CB00F6BD48 /* SDWebImageTestTransformer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageTestTransformer.m; sourceTree = "<group>"; };
327054E1206CEFF3006EA328 /* TestImageAnimated.apng */ = {isa = PBXFileReference; lastKnownFileType = file; path = TestImageAnimated.apng; sourceTree = "<group>"; };
328BB6DB20825E9800760D6C /* SDWebImageTestCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebImageTestCache.h; sourceTree = "<group>"; };
328BB6DC20825E9800760D6C /* SDWebImageTestCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageTestCache.m; sourceTree = "<group>"; };
32A571552037DB2D002EDAAE /* SDAnimatedImageTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDAnimatedImageTest.m; sourceTree = "<group>"; };
32B99E8A203AF8690017FD66 /* SDCategoriesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDCategoriesTests.m; sourceTree = "<group>"; };
32B99E92203B2DF90017FD66 /* Tests Mac.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tests Mac.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
@ -211,6 +215,8 @@
37D122871EC48B5E00D98CEB /* SDMockFileManager.m */,
2D7AF05E1F329763000083C2 /* SDTestCase.h */,
2D7AF05F1F329763000083C2 /* SDTestCase.m */,
328BB6DB20825E9800760D6C /* SDWebImageTestCache.h */,
328BB6DC20825E9800760D6C /* SDWebImageTestCache.m */,
3226ECB920754F7700FAFACF /* SDWebImageTestDownloadOperation.h */,
3226ECBA20754F7700FAFACF /* SDWebImageTestDownloadOperation.m */,
32E6F0301F3A1B4700A945E6 /* SDWebImageTestDecoder.h */,
@ -466,6 +472,7 @@
files = (
32B99EAC203B36650017FD66 /* SDWebImageDownloaderTests.m in Sources */,
3254C32120641077008D1022 /* SDWebImageTransformerTests.m in Sources */,
328BB6DE20825E9800760D6C /* SDWebImageTestCache.m in Sources */,
32B99E9C203B2EE40017FD66 /* SDCategoriesTests.m in Sources */,
32B99EAA203B365F0017FD66 /* SDImageCacheTests.m in Sources */,
32B99EAD203B36690017FD66 /* SDWebImagePrefetcherTests.m in Sources */,
@ -493,6 +500,7 @@
37D122881EC48B5E00D98CEB /* SDMockFileManager.m in Sources */,
4369C2741D9804B1007E863A /* SDWebCacheCategoriesTests.m in Sources */,
2D7AF0601F329763000083C2 /* SDTestCase.m in Sources */,
328BB6DD20825E9800760D6C /* SDWebImageTestCache.m in Sources */,
4369C1D11D97F80F007E863A /* SDWebImagePrefetcherTests.m in Sources */,
DA248D69195475D800390AB0 /* SDImageCacheTests.m in Sources */,
DA248D6B195476AC00390AB0 /* SDWebImageManagerTests.m in Sources */,

View File

@ -11,9 +11,17 @@
#import <SDWebImage/SDWebImageCodersManager.h>
#import "SDWebImageTestDecoder.h"
#import "SDMockFileManager.h"
#import "SDWebImageTestCache.h"
NSString *kImageTestKey = @"TestImageKey.jpg";
@interface SDImageCache ()
@property (nonatomic, strong, nonnull) id<SDMemoryCache> memCache;
@property (nonatomic, strong, nonnull) id<SDDiskCache> diskCache;
@end
@interface SDImageCacheTests : SDTestCase
@end
@ -314,13 +322,36 @@ NSString *kImageTestKey = @"TestImageKey.jpg";
fileManager.mockSelectors = @{NSStringFromSelector(@selector(createDirectoryAtPath:withIntermediateDirectories:attributes:error:)) : targetError};
expect(fileManager.lastError).to.beNil();
SDImageCacheConfig *config = [SDImageCacheConfig new];
config.fileManager = fileManager;
// This disk cache path creation will be mocked with error.
SDImageCache *cache = [[SDImageCache alloc] initWithNamespace:@"test" diskCacheDirectory:@"/" fileManager:fileManager];
SDImageCache *cache = [[SDImageCache alloc] initWithNamespace:@"test" diskCacheDirectory:@"/" config:config];
[cache storeImageDataToDisk:imageData
forKey:kImageTestKey];
expect(fileManager.lastError).equal(targetError);
}
#pragma mark - SDMemoryCache & SDDiskCache
- (void)test42CustomMemoryCache {
SDImageCacheConfig *config = [[SDImageCacheConfig alloc] init];
config.memoryCacheClass = [SDWebImageTestMemoryCache class];
NSString *nameSpace = @"SDWebImageTestMemoryCache";
NSString *cacheDictionary = [self makeDiskCachePath:nameSpace];
SDImageCache *cache = [[SDImageCache alloc] initWithNamespace:nameSpace diskCacheDirectory:cacheDictionary config:config];
SDWebImageTestMemoryCache *memCache = cache.memCache;
expect([memCache isKindOfClass:[SDWebImageTestMemoryCache class]]).to.beTruthy();
}
- (void)test43CustomDiskCache {
SDImageCacheConfig *config = [[SDImageCacheConfig alloc] init];
config.diskCacheClass = [SDWebImageTestDiskCache class];
NSString *nameSpace = @"SDWebImageTestDiskCache";
NSString *cacheDictionary = [self makeDiskCachePath:nameSpace];
SDImageCache *cache = [[SDImageCache alloc] initWithNamespace:nameSpace diskCacheDirectory:cacheDictionary config:config];
SDWebImageTestDiskCache *diskCache = cache.diskCache;
expect([diskCache isKindOfClass:[SDWebImageTestDiskCache class]]).to.beTruthy();
}
#pragma mark Helper methods
- (UIImage *)imageForTesting{
@ -337,4 +368,9 @@ NSString *kImageTestKey = @"TestImageKey.jpg";
return [testBundle pathForResource:@"TestImage" ofType:@"jpg"];
}
- (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace {
NSArray<NSString *> *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
return [paths[0] stringByAppendingPathComponent:fullNamespace];
}
@end

View File

@ -0,0 +1,28 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
* (c) Matt Galloway
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import <SDWebImage/SDMemoryCache.h>
#import <SDWebImage/SDDiskCache.h>
// A really naive implementation of custom memory cache and disk cache
@interface SDWebImageTestMemoryCache : NSObject <SDMemoryCache>
@property (nonatomic, strong, nonnull) SDImageCacheConfig *config;
@property (nonatomic, strong, nonnull) NSCache *cache;
@end
@interface SDWebImageTestDiskCache : NSObject <SDDiskCache>
@property (nonatomic, strong, nonnull) SDImageCacheConfig *config;
@property (nonatomic, copy, nonnull) NSString *cachePath;
@property (nonatomic, strong, nonnull) NSFileManager *fileManager;
@end

View File

@ -0,0 +1,107 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
* (c) Matt Galloway
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDWebImageTestCache.h"
#import <SDWebImage/SDImageCacheConfig.h>
@implementation SDWebImageTestMemoryCache
- (nonnull instancetype)initWithConfig:(nonnull SDImageCacheConfig *)config {
self = [super init];
if (self) {
self.config = config;
self.cache = [[NSCache alloc] init];
}
return self;
}
- (nullable id)objectForKey:(nonnull id)key {
return [self.cache objectForKey:key];
}
- (void)removeAllObjects {
[self.cache removeAllObjects];
}
- (void)removeObjectForKey:(nonnull id)key {
[self.cache removeObjectForKey:key];
}
- (void)setObject:(nullable id)object forKey:(nonnull id)key {
[self.cache setObject:object forKey:key];
}
- (void)setObject:(nullable id)object forKey:(nonnull id)key cost:(NSUInteger)cost {
[self.cache setObject:object forKey:key cost:cost];
}
@end
@implementation SDWebImageTestDiskCache
- (nullable NSString *)cachePathForKey:(nonnull NSString *)key {
return [self.cachePath stringByAppendingPathComponent:key];
}
- (BOOL)containsDataForKey:(nonnull NSString *)key {
return [self.fileManager fileExistsAtPath:[self cachePathForKey:key]];
}
- (nullable NSData *)dataForKey:(nonnull NSString *)key {
return [self.fileManager contentsAtPath:[self cachePathForKey:key]];
}
- (nullable instancetype)initWithCachePath:(nonnull NSString *)cachePath config:(nonnull SDImageCacheConfig *)config {
self = [super init];
if (self) {
self.cachePath = cachePath;
self.config = config;
self.fileManager = config.fileManager ? config.fileManager : [NSFileManager new];
[self.fileManager createDirectoryAtPath:self.cachePath withIntermediateDirectories:YES attributes:nil error:nil];
}
return self;
}
- (void)removeAllData {
[self.fileManager removeItemAtPath:self.cachePath error:nil];
}
- (void)removeDataForKey:(nonnull NSString *)key {
[self.fileManager removeItemAtPath:[self cachePathForKey:key] error:nil];
}
- (void)removeExpiredData {
NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.config.maxCacheAge];
for (NSString *fileName in [self.fileManager enumeratorAtPath:self.cachePath]) {
NSString *filePath = [self.cachePath stringByAppendingPathComponent:fileName];
NSDate *modificationDate = [[self.fileManager attributesOfItemAtPath:filePath error:nil] objectForKey:NSFileModificationDate];
if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
[self.fileManager removeItemAtPath:filePath error:nil];
}
}
}
- (void)setData:(nullable NSData *)data forKey:(nonnull NSString *)key {
[self.fileManager createFileAtPath:[self cachePathForKey:key] contents:data attributes:nil];
}
- (NSInteger)totalCount {
return [self.fileManager contentsOfDirectoryAtPath:self.cachePath error:nil].count;
}
- (NSInteger)totalSize {
NSUInteger size = 0;
for (NSString *fileName in [self.fileManager enumeratorAtPath:self.cachePath]) {
NSString *filePath = [self.cachePath stringByAppendingPathComponent:fileName];
size += [[[self.fileManager attributesOfItemAtPath:filePath error:nil] objectForKey:NSFileSize] unsignedIntegerValue];
}
return size;
}
@end

View File

@ -26,6 +26,8 @@ FOUNDATION_EXPORT const unsigned char WebImageVersionString[];
#import <SDWebImage/SDWebImageCacheSerializer.h>
#import <SDWebImage/SDImageCacheConfig.h>
#import <SDWebImage/SDImageCache.h>
#import <SDWebImage/SDMemoryCache.h>
#import <SDWebImage/SDDiskCache.h>
#import <SDWebImage/UIView+WebCache.h>
#import <SDWebImage/UIImageView+WebCache.h>
#import <SDWebImage/UIImageView+HighlightedWebCache.h>