Add memory cache and disk cache protocol, make our image cache just like a wrapper to call the disk cache implementation
This commit is contained in:
parent
c5647c8f8a
commit
62058d209d
|
@ -388,6 +388,30 @@
|
|||
327054DD206CD8B3006EA328 /* SDWebImageAPNGCoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 327054D3206CD8B3006EA328 /* SDWebImageAPNGCoder.m */; };
|
||||
327054DE206CD8B3006EA328 /* SDWebImageAPNGCoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 327054D3206CD8B3006EA328 /* SDWebImageAPNGCoder.m */; };
|
||||
327054DF206CD8B3006EA328 /* SDWebImageAPNGCoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 327054D3206CD8B3006EA328 /* SDWebImageAPNGCoder.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, ); }; };
|
||||
|
@ -1492,6 +1516,10 @@
|
|||
325312C7200F09910046BF1E /* SDWebImageTransition.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageTransition.m; sourceTree = "<group>"; };
|
||||
327054D2206CD8B3006EA328 /* SDWebImageAPNGCoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebImageAPNGCoder.h; sourceTree = "<group>"; };
|
||||
327054D3206CD8B3006EA328 /* SDWebImageAPNGCoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageAPNGCoder.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>"; };
|
||||
|
@ -1985,6 +2013,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>";
|
||||
|
@ -2238,6 +2270,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 */,
|
||||
|
@ -2246,6 +2279,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 */,
|
||||
|
@ -2356,6 +2390,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 */,
|
||||
|
@ -2364,6 +2399,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 */,
|
||||
|
@ -2421,6 +2457,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 */,
|
||||
|
@ -2432,6 +2469,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 */,
|
||||
|
@ -2505,6 +2543,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 */,
|
||||
|
@ -2513,6 +2552,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 */,
|
||||
|
@ -2597,6 +2637,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 */,
|
||||
|
@ -2605,6 +2646,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 */,
|
||||
|
@ -2663,6 +2705,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 */,
|
||||
|
@ -2682,6 +2725,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 */,
|
||||
80377CEF1F2F66A100F89830 /* dsp.h in Headers */,
|
||||
32F21B5120788D8C0036B1D5 /* SDWebImageDownloaderRequestModifier.h in Headers */,
|
||||
|
@ -2984,6 +3028,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 */,
|
||||
|
@ -3032,6 +3077,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 */,
|
||||
|
@ -3194,6 +3240,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 */,
|
||||
|
@ -3232,6 +3279,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 */,
|
||||
|
@ -3351,6 +3399,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 */,
|
||||
|
@ -3389,6 +3438,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 */,
|
||||
|
@ -3454,6 +3504,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 */,
|
||||
|
@ -3476,6 +3527,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 */,
|
||||
80377ED71F2F66D500F89830 /* alpha_dec.c in Sources */,
|
||||
323F8B7F1F38EF770092B609 /* frame_enc.c in Sources */,
|
||||
|
@ -3618,6 +3670,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 */,
|
||||
|
@ -3666,6 +3719,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 */,
|
||||
|
@ -3779,6 +3833,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 */,
|
||||
|
@ -3827,6 +3882,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 */,
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
@ -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
|
||||
|
|
|
@ -7,21 +7,60 @@
|
|||
*/
|
||||
|
||||
#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.shouldDecompressImages = self.shouldDecompressImages;
|
||||
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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -314,8 +314,10 @@ 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);
|
||||
|
|
|
@ -24,6 +24,8 @@ FOUNDATION_EXPORT const unsigned char WebImageVersionString[];
|
|||
#import <SDWebImage/SDWebImageManager.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>
|
||||
|
|
Loading…
Reference in New Issue