├── .DS_Store ├── .gitattributes ├── SDWebImageComment ├── .DS_Store ├── WebImage │ ├── .DS_Store │ ├── Other │ │ ├── .DS_Store │ │ ├── SDWebImageOperation.h │ │ ├── SDWebImage.h │ │ ├── SDWebImageCompat.m │ │ └── SDWebImageCompat.h │ ├── SupportFiles │ │ ├── Assets.xcassets │ │ │ ├── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── ViewController.swift │ │ ├── Base.lproj │ │ │ ├── Main.storyboard │ │ │ └── LaunchScreen.storyboard │ │ ├── Info.plist │ │ └── AppDelegate.swift │ ├── UI_Categories │ │ ├── .DS_Store │ │ ├── NSImage+WebCache.h │ │ ├── NSImage+WebCache.m │ │ ├── UIImageView+HighlightedWebCache.m │ │ ├── MKAnnotationView+WebCache.m │ │ ├── UIView+WebCache.h │ │ ├── UIImageView+HighlightedWebCache.h │ │ ├── MKAnnotationView+WebCache.h │ │ ├── UIImageView+WebCache.m │ │ ├── UIButton+WebCache.m │ │ ├── UIView+WebCache.m │ │ ├── UIImageView+WebCache.h │ │ └── UIButton+WebCache.h │ ├── Categories │ │ ├── UIImage+WebP.h │ │ ├── UIImage+MultiFormat.h │ │ ├── UIImage+GIF.h │ │ ├── NSData+ImageContentType.h │ │ ├── UIView+WebCacheOperation.h │ │ ├── NSData+ImageContentType.m │ │ ├── UIImage+GIF.m │ │ ├── UIView+WebCacheOperation.m │ │ ├── UIImage+MultiFormat.m │ │ └── UIImage+WebP.m │ ├── Utils │ │ ├── SDWebImageDecoder.h │ │ ├── SDWebImagePrefetcher.h │ │ ├── SDWebImagePrefetcher.m │ │ ├── SDWebImageManager.h │ │ ├── SDWebImageDecoder.m │ │ └── SDWebImageManager.m │ ├── Cache │ │ ├── SDImageCacheConfig.m │ │ ├── SDImageCacheConfig.h │ │ └── SDImageCache.h │ └── Downloader │ │ ├── SDWebImageDownloaderOperation.h │ │ ├── SDWebImageDownloader.h │ │ └── SDWebImageDownloader.m └── SDWebImageComment.xcodeproj │ └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── .gitignore └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YTiOSer/SDWebImage_Comment/HEAD/.DS_Store -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /SDWebImageComment/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YTiOSer/SDWebImage_Comment/HEAD/SDWebImageComment/.DS_Store -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YTiOSer/SDWebImage_Comment/HEAD/SDWebImageComment/WebImage/.DS_Store -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Other/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YTiOSer/SDWebImage_Comment/HEAD/SDWebImageComment/WebImage/Other/.DS_Store -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/SupportFiles/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/UI_Categories/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YTiOSer/SDWebImage_Comment/HEAD/SDWebImageComment/WebImage/UI_Categories/.DS_Store -------------------------------------------------------------------------------- /SDWebImageComment/SDWebImageComment.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SDWebImageComment/SDWebImageComment.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Other/SDWebImageOperation.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import 10 | //SDWebImage的Operation接口 11 | @protocol SDWebImageOperation 12 | 13 | - (void)cancel; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Categories/UIImage+WebP.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #ifdef SD_WEBP 10 | 11 | #import "SDWebImageCompat.h" 12 | 13 | @interface UIImage (WebP) 14 | 15 | + (nullable UIImage *)sd_imageWithWebPData:(nullable NSData *)data; 16 | 17 | @end 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/UI_Categories/NSImage+WebCache.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDWebImageCompat.h" 10 | 11 | #if SD_MAC 12 | 13 | #import 14 | 15 | @interface NSImage (WebCache) 16 | 17 | - (CGImageRef)CGImage; 18 | - (NSArray *)images; 19 | - (BOOL)isGIF; 20 | 21 | @end 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Categories/UIImage+MultiFormat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDWebImageCompat.h" 10 | #import "NSData+ImageContentType.h" 11 | 12 | @interface UIImage (MultiFormat) 13 | 14 | + (nullable UIImage *)sd_imageWithData:(nullable NSData *)data; 15 | - (nullable NSData *)sd_imageData; 16 | - (nullable NSData *)sd_imageDataAsFormat:(SDImageFormat)imageFormat; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Utils/SDWebImageDecoder.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * (c) james 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | #import 11 | #import "SDWebImageCompat.h" 12 | 13 | @interface UIImage (ForceDecode) 14 | 15 | + (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image; 16 | 17 | + (nullable UIImage *)decodedAndScaledDownImageWithImage:(nullable UIImage *)image; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/SupportFiles/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // SDWebImageComment 4 | // 5 | // Created by yangtao on 2018/5/16. 6 | // Copyright © 2018年 qeegoo. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view, typically from a nib. 16 | } 17 | 18 | override func didReceiveMemoryWarning() { 19 | super.didReceiveMemoryWarning() 20 | // Dispose of any resources that can be recreated. 21 | } 22 | 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Cache/SDImageCacheConfig.m: -------------------------------------------------------------------------------- 1 | // 2 | // SDImageCacheConfig.m 3 | // SDWebImage 4 | // 5 | // Created by Bogdan on 09/09/16. 6 | // Copyright © 2016 Dailymotion. All rights reserved. 7 | // 8 | 9 | #import "SDImageCacheConfig.h" 10 | 11 | static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week 12 | 13 | @implementation SDImageCacheConfig 14 | 15 | - (instancetype)init { 16 | if (self = [super init]) { 17 | _shouldDecompressImages = YES; 18 | _shouldDisableiCloud = YES; 19 | _shouldCacheImagesInMemory = YES; 20 | _maxCacheAge = kDefaultCacheMaxCacheAge; 21 | _maxCacheSize = 0; 22 | } 23 | return self; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Categories/UIImage+GIF.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * (c) Laurin Brandner 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | #import "SDWebImageCompat.h" 11 | 12 | @interface UIImage (GIF) 13 | 14 | /** 15 | * Compatibility method - creates an animated UIImage from an NSData, it will only contain the 1st frame image 16 | */ 17 | + (UIImage *)sd_animatedGIFWithData:(NSData *)data; 18 | 19 | /** 20 | * Checks if an UIImage instance is a GIF. Will use the `images` array 21 | */ 22 | - (BOOL)isGIF; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/UI_Categories/NSImage+WebCache.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "NSImage+WebCache.h" 10 | 11 | #if SD_MAC 12 | 13 | @implementation NSImage (WebCache) 14 | 15 | - (CGImageRef)CGImage { 16 | NSRect imageRect = NSMakeRect(0, 0, self.size.width, self.size.height); 17 | CGImageRef cgImage = [self CGImageForProposedRect:&imageRect context:NULL hints:nil]; 18 | return cgImage; 19 | } 20 | 21 | - (NSArray *)images { 22 | return nil; 23 | } 24 | 25 | - (BOOL)isGIF { 26 | return NO; 27 | } 28 | 29 | @end 30 | 31 | #endif 32 | 33 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Categories/NSData+ImageContentType.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * (c) Fabrice Aneche 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | #import 11 | #import "SDWebImageCompat.h" 12 | 13 | typedef NS_ENUM(NSInteger, SDImageFormat) { 14 | SDImageFormatUndefined = -1, 15 | SDImageFormatJPEG = 0, 16 | SDImageFormatPNG, 17 | SDImageFormatGIF, 18 | SDImageFormatTIFF, 19 | SDImageFormatWebP 20 | }; 21 | 22 | @interface NSData (ImageContentType) 23 | 24 | /** 25 | * Return image format 26 | * 27 | * @param data the input image data 28 | * 29 | * @return the image format as `SDImageFormat` (enum) 30 | */ 31 | + (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Cache/SDImageCacheConfig.h: -------------------------------------------------------------------------------- 1 | // 2 | // SDImageCacheConfig.h 3 | // SDWebImage 4 | // 5 | // Created by Bogdan on 09/09/16. 6 | // Copyright © 2016 Dailymotion. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SDWebImageCompat.h" 11 | 12 | @interface SDImageCacheConfig : NSObject 13 | 14 | /** 15 | * Decompressing images that are downloaded and cached can improve performance but can consume lot of memory. 16 | * Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption. 17 | */ 18 | @property (assign, nonatomic) BOOL shouldDecompressImages; 19 | 20 | /** 21 | * disable iCloud backup [defaults to YES] 22 | */ 23 | @property (assign, nonatomic) BOOL shouldDisableiCloud; 24 | 25 | /** 26 | * use memory cache [defaults to YES] 27 | */ 28 | @property (assign, nonatomic) BOOL shouldCacheImagesInMemory; 29 | 30 | /** 31 | * The maximum length of time to keep an image in the cache, in seconds 32 | */ 33 | @property (assign, nonatomic) NSInteger maxCacheAge; 34 | 35 | /** 36 | * The maximum size of the cache, in bytes. 37 | */ 38 | @property (assign, nonatomic) NSUInteger maxCacheSize; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Categories/UIView+WebCacheOperation.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDWebImageCompat.h" 10 | 11 | #if SD_UIKIT || SD_MAC 12 | 13 | #import "SDWebImageManager.h" 14 | 15 | @interface UIView (WebCacheOperation) 16 | 17 | /** 18 | * Set the image load operation (storage in a UIView based dictionary) 19 | * 20 | * @param operation the operation 21 | * @param key key for storing the operation 22 | */ 23 | - (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key; 24 | 25 | /** 26 | * Cancel all operations for the current UIView and key 27 | * 28 | * @param key key for identifying the operations 29 | */ 30 | - (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key; 31 | 32 | /** 33 | * Just remove the operations corresponding to the current UIView and key without cancelling them 34 | * 35 | * @param key key for identifying the operations 36 | */ 37 | - (void)sd_removeImageLoadOperationWithKey:(nullable NSString *)key; 38 | 39 | @end 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Categories/NSData+ImageContentType.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * (c) Fabrice Aneche 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | #import "NSData+ImageContentType.h" 11 | 12 | 13 | @implementation NSData (ImageContentType) 14 | 15 | //识别图片格式 16 | + (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data { 17 | if (!data) { 18 | return SDImageFormatUndefined; 19 | } 20 | 21 | uint8_t c; 22 | //读取图片文件头识别图片格式 23 | //Copies a number of bytes from the start of the receiver's data into a given buffer. 24 | [data getBytes:&c length:1]; 25 | switch (c) { 26 | case 0xFF: 27 | return SDImageFormatJPEG; 28 | case 0x89: 29 | return SDImageFormatPNG; 30 | case 0x47: 31 | return SDImageFormatGIF; 32 | case 0x49: 33 | case 0x4D: 34 | return SDImageFormatTIFF; 35 | case 0x52: 36 | // R as RIFF for WEBP 37 | if (data.length < 12) { 38 | return SDImageFormatUndefined; 39 | } 40 | 41 | NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding]; 42 | if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) { 43 | return SDImageFormatWebP; 44 | } 45 | } 46 | return SDImageFormatUndefined; 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | # Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/SupportFiles/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/SupportFiles/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | SDWebImageComment 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/SupportFiles/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Other/SDWebImage.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * (c) Florent Vilmart 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | #import 11 | 12 | #if SD_UIKIT 13 | #import 14 | #endif 15 | 16 | //! Project version number for WebImage. 17 | FOUNDATION_EXPORT double WebImageVersionNumber; 18 | 19 | //! Project version string for WebImage. 20 | FOUNDATION_EXPORT const unsigned char WebImageVersionString[]; 21 | 22 | // In this header, you should import all the public headers of your framework using statements like #import 23 | 24 | #import 25 | #import 26 | #import 27 | #import 28 | #import 29 | #import 30 | #import 31 | #import 32 | #import 33 | #import 34 | #import 35 | #import 36 | #import 37 | #if SD_MAC || SD_UIKIT 38 | #import 39 | #endif 40 | #import 41 | #import 42 | #import 43 | #import 44 | #if SD_MAC 45 | #import 46 | #endif 47 | #if SD_UIKIT 48 | #import 49 | #endif 50 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/SupportFiles/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Categories/UIImage+GIF.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * (c) Laurin Brandner 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | #import "UIImage+GIF.h" 11 | #import 12 | #import "objc/runtime.h" 13 | #import "NSImage+WebCache.h" 14 | 15 | @implementation UIImage (GIF) 16 | // 返回静态图片 17 | // 在4.0版本之后 GIF 图片使用 FLAnimatedImageView 来支持,SDWEBImage对GIF的处理都是只取第一帧图片 18 | + (UIImage *)sd_animatedGIFWithData:(NSData *)data { 19 | if (!data) { 20 | return nil; 21 | } 22 | 23 | CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); 24 | 25 | size_t count = CGImageSourceGetCount(source); 26 | 27 | UIImage *staticImage; 28 | 29 | if (count <= 1) { 30 | staticImage = [[UIImage alloc] initWithData:data]; 31 | } else { 32 | // we will only retrieve the 1st frame. the full GIF support is available via the FLAnimatedImageView category. 33 | // this here is only code to allow drawing animated images as static ones 34 | #if SD_WATCH 35 | CGFloat scale = 1; 36 | scale = [WKInterfaceDevice currentDevice].screenScale; 37 | #elif SD_UIKIT 38 | // 缩放倍数 39 | CGFloat scale = 1; 40 | scale = [UIScreen mainScreen].scale; 41 | #endif 42 | //取第一帧图片 43 | CGImageRef CGImage = CGImageSourceCreateImageAtIndex(source, 0, NULL); 44 | #if SD_UIKIT || SD_WATCH 45 | UIImage *frameImage = [UIImage imageWithCGImage:CGImage scale:scale orientation:UIImageOrientationUp]; 46 | staticImage = [UIImage animatedImageWithImages:@[frameImage] duration:0.0f]; 47 | #elif SD_MAC 48 | staticImage = [[UIImage alloc] initWithCGImage:CGImage size:NSZeroSize]; 49 | #endif 50 | CGImageRelease(CGImage); 51 | } 52 | 53 | CFRelease(source); 54 | // 返回静态图片 55 | return staticImage; 56 | } 57 | 58 | - (BOOL)isGIF { 59 | return (self.images != nil); 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Other/SDWebImageCompat.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDWebImageCompat.h" 10 | 11 | #if !__has_feature(objc_arc) 12 | #error SDWebImage is ARC only. Either turn on ARC for the project or use -fobjc-arc flag 13 | #endif 14 | 15 | // 返回正确的缩放倍数图片 16 | inline UIImage *SDScaledImageForKey(NSString * _Nullable key, UIImage * _Nullable image) { 17 | if (!image) { 18 | return nil; 19 | } 20 | 21 | #if SD_MAC 22 | return image; 23 | #elif SD_UIKIT || SD_WATCH 24 | // 动图处理 25 | if ((image.images).count > 0) { 26 | NSMutableArray *scaledImages = [NSMutableArray array]; 27 | 28 | for (UIImage *tempImage in image.images) { 29 | //递归处理 30 | [scaledImages addObject:SDScaledImageForKey(key, tempImage)]; 31 | } 32 | // 处理动图播放 33 | return [UIImage animatedImageWithImages:scaledImages duration:image.duration]; 34 | } 35 | else { 36 | #if SD_WATCH 37 | if ([[WKInterfaceDevice currentDevice] respondsToSelector:@selector(screenScale)]) { 38 | #elif SD_UIKIT 39 | if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) { 40 | #endif 41 | //判断图片的缩放倍数,默认为 1 42 | CGFloat scale = 1; 43 | // 判断key的命名规则是否有包含缩放倍数 44 | if (key.length >= 8) { 45 | NSRange range = [key rangeOfString:@"@2x."]; 46 | if (range.location != NSNotFound) { 47 | scale = 2.0; 48 | } 49 | 50 | range = [key rangeOfString:@"@3x."]; 51 | if (range.location != NSNotFound) { 52 | scale = 3.0; 53 | } 54 | } 55 | //创建UIImage对象 56 | UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation]; 57 | image = scaledImage; 58 | } 59 | return image; 60 | } 61 | #endif 62 | } 63 | 64 | NSString *const SDWebImageErrorDomain = @"SDWebImageErrorDomain"; 65 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/UI_Categories/UIImageView+HighlightedWebCache.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "UIImageView+HighlightedWebCache.h" 10 | 11 | #if SD_UIKIT 12 | 13 | #import "UIView+WebCacheOperation.h" 14 | #import "UIView+WebCache.h" 15 | 16 | @implementation UIImageView (HighlightedWebCache) 17 | 18 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url { 19 | [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:nil]; 20 | } 21 | 22 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url options:(SDWebImageOptions)options { 23 | [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:nil]; 24 | } 25 | 26 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url completed:(nullable SDExternalCompletionBlock)completedBlock { 27 | [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:completedBlock]; 28 | } 29 | 30 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url options:(SDWebImageOptions)options completed:(nullable SDExternalCompletionBlock)completedBlock { 31 | [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:completedBlock]; 32 | } 33 | 34 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url 35 | options:(SDWebImageOptions)options 36 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 37 | completed:(nullable SDExternalCompletionBlock)completedBlock { 38 | __weak typeof(self)weakSelf = self; 39 | [self sd_internalSetImageWithURL:url 40 | placeholderImage:nil 41 | options:options 42 | operationKey:@"UIImageViewImageOperationHighlighted" 43 | setImageBlock:^(UIImage *image, NSData *imageData) { 44 | weakSelf.highlightedImage = image; 45 | } 46 | progress:progressBlock 47 | completed:completedBlock]; 48 | } 49 | 50 | @end 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/SupportFiles/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SDWebImageComment 4 | // 5 | // Created by yangtao on 2018/5/16. 6 | // Copyright © 2018年 qeegoo. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/UI_Categories/MKAnnotationView+WebCache.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "MKAnnotationView+WebCache.h" 10 | 11 | #if SD_UIKIT || SD_MAC 12 | 13 | #import "objc/runtime.h" 14 | #import "UIView+WebCacheOperation.h" 15 | #import "UIView+WebCache.h" 16 | 17 | @implementation MKAnnotationView (WebCache) 18 | 19 | - (void)sd_setImageWithURL:(nullable NSURL *)url { 20 | [self sd_setImageWithURL:url placeholderImage:nil options:0 completed:nil]; 21 | } 22 | 23 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder { 24 | [self sd_setImageWithURL:url placeholderImage:placeholder options:0 completed:nil]; 25 | } 26 | 27 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options { 28 | [self sd_setImageWithURL:url placeholderImage:placeholder options:options completed:nil]; 29 | } 30 | 31 | - (void)sd_setImageWithURL:(nullable NSURL *)url completed:(nullable SDExternalCompletionBlock)completedBlock { 32 | [self sd_setImageWithURL:url placeholderImage:nil options:0 completed:completedBlock]; 33 | } 34 | 35 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock { 36 | [self sd_setImageWithURL:url placeholderImage:placeholder options:0 completed:completedBlock]; 37 | } 38 | 39 | - (void)sd_setImageWithURL:(nullable NSURL *)url 40 | placeholderImage:(nullable UIImage *)placeholder 41 | options:(SDWebImageOptions)options 42 | completed:(nullable SDExternalCompletionBlock)completedBlock { 43 | __weak typeof(self)weakSelf = self; 44 | [self sd_internalSetImageWithURL:url 45 | placeholderImage:placeholder 46 | options:options 47 | operationKey:nil 48 | setImageBlock:^(UIImage *image, NSData *imageData) { 49 | weakSelf.image = image; 50 | } 51 | progress:nil 52 | completed:completedBlock]; 53 | } 54 | 55 | @end 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Categories/UIView+WebCacheOperation.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "UIView+WebCacheOperation.h" 10 | 11 | #if SD_UIKIT || SD_MAC 12 | 13 | #import "objc/runtime.h" 14 | 15 | static char loadOperationKey; 16 | 17 | typedef NSMutableDictionary SDOperationsDictionary; 18 | 19 | @implementation UIView (WebCacheOperation) 20 | 21 | - (SDOperationsDictionary *)operationDictionary { 22 | SDOperationsDictionary *operations = objc_getAssociatedObject(self, &loadOperationKey); 23 | if (operations) { 24 | return operations; 25 | } 26 | operations = [NSMutableDictionary dictionary]; 27 | objc_setAssociatedObject(self, &loadOperationKey, operations, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 28 | return operations; 29 | } 30 | 31 | - (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key { 32 | if (key) { 33 | [self sd_cancelImageLoadOperationWithKey:key]; 34 | if (operation) { 35 | SDOperationsDictionary *operationDictionary = [self operationDictionary]; 36 | operationDictionary[key] = operation; 37 | } 38 | } 39 | } 40 | 41 | // 取消图片加载操作 42 | - (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key { 43 | // Cancel in progress downloader from queue 44 | SDOperationsDictionary *operationDictionary = [self operationDictionary]; //获取UIView上动态添加的属性 45 | id operations = operationDictionary[key]; 46 | if (operations) { //如果有对应的加载操作 47 | if ([operations isKindOfClass:[NSArray class]]) { 48 | // SDWebImageOperation数组, 将数组中的每个加载操作都取消 49 | for (id operation in operations) { 50 | if (operation) { 51 | [operation cancel]; 52 | } 53 | } 54 | } else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){ 55 | //实现 SDWebImageOperation 协议 56 | [(id) operations cancel]; 57 | } 58 | [operationDictionary removeObjectForKey:key]; //取消后 移除这个属性 59 | } 60 | } 61 | 62 | - (void)sd_removeImageLoadOperationWithKey:(nullable NSString *)key { 63 | if (key) { 64 | SDOperationsDictionary *operationDictionary = [self operationDictionary]; 65 | [operationDictionary removeObjectForKey:key]; 66 | } 67 | } 68 | 69 | @end 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/UI_Categories/UIView+WebCache.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDWebImageCompat.h" 10 | 11 | #if SD_UIKIT || SD_MAC 12 | 13 | #import "SDWebImageManager.h" 14 | 15 | typedef void(^SDSetImageBlock)(UIImage * _Nullable image, NSData * _Nullable imageData); 16 | 17 | @interface UIView (WebCache) 18 | 19 | /** 20 | * Get the current image URL. 21 | * 22 | * Note that because of the limitations of categories this property can get out of sync 23 | * if you use setImage: directly. 24 | */ 25 | - (nullable NSURL *)sd_imageURL; 26 | 27 | /** 28 | * Set the imageView `image` with an `url` and optionally a placeholder image. 29 | * 30 | * The download is asynchronous and cached. 31 | * 32 | * @param url The url for the image. 33 | * @param placeholder The image to be set initially, until the image request finishes. 34 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. 35 | * @param operationKey A string to be used as the operation key. If nil, will use the class name 36 | * @param setImageBlock Block used for custom set image code 37 | * @param progressBlock A block called while image is downloading 38 | * @note the progress block is executed on a background queue 39 | * @param completedBlock A block called when operation has been completed. This block has no return value 40 | * and takes the requested UIImage as first parameter. In case of error the image parameter 41 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 42 | * indicating if the image was retrieved from the local cache or from the network. 43 | * The fourth parameter is the original image url. 44 | */ 45 | - (void)sd_internalSetImageWithURL:(nullable NSURL *)url 46 | placeholderImage:(nullable UIImage *)placeholder 47 | options:(SDWebImageOptions)options 48 | operationKey:(nullable NSString *)operationKey 49 | setImageBlock:(nullable SDSetImageBlock)setImageBlock 50 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 51 | completed:(nullable SDExternalCompletionBlock)completedBlock; 52 | 53 | /** 54 | * Cancel the current download 55 | */ 56 | - (void)sd_cancelCurrentImageLoad; 57 | 58 | #if SD_UIKIT 59 | 60 | #pragma mark - Activity indicator 61 | 62 | /** 63 | * Show activity UIActivityIndicatorView 64 | */ 65 | - (void)sd_setShowActivityIndicatorView:(BOOL)show; 66 | 67 | /** 68 | * set desired UIActivityIndicatorViewStyle 69 | * 70 | * @param style The style of the UIActivityIndicatorView 71 | */ 72 | - (void)sd_setIndicatorStyle:(UIActivityIndicatorViewStyle)style; 73 | 74 | - (BOOL)sd_showActivityIndicatorView; 75 | - (void)sd_addActivityIndicator; 76 | - (void)sd_removeActivityIndicator; 77 | 78 | #endif 79 | 80 | @end 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Other/SDWebImageCompat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * (c) Jamie Pinkham 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | #import 11 | 12 | #ifdef __OBJC_GC__ 13 | #error SDWebImage does not support Objective-C Garbage Collection 14 | #endif 15 | 16 | // Apple's defines from TargetConditionals.h are a bit weird. 17 | // Seems like TARGET_OS_MAC is always defined (on all platforms). 18 | // To determine if we are running on OSX, we can only relly on TARGET_OS_IPHONE=0 and all the other platforms 19 | #if !TARGET_OS_IPHONE && !TARGET_OS_IOS && !TARGET_OS_TV && !TARGET_OS_WATCH 20 | #define SD_MAC 1 21 | #else 22 | #define SD_MAC 0 23 | #endif 24 | 25 | // iOS and tvOS are very similar, UIKit exists on both platforms 26 | // Note: watchOS also has UIKit, but it's very limited 27 | #if TARGET_OS_IOS || TARGET_OS_TV 28 | #define SD_UIKIT 1 29 | #else 30 | #define SD_UIKIT 0 31 | #endif 32 | 33 | #if TARGET_OS_IOS 34 | #define SD_IOS 1 35 | #else 36 | #define SD_IOS 0 37 | #endif 38 | 39 | #if TARGET_OS_TV 40 | #define SD_TV 1 41 | #else 42 | #define SD_TV 0 43 | #endif 44 | 45 | #if TARGET_OS_WATCH 46 | #define SD_WATCH 1 47 | #else 48 | #define SD_WATCH 0 49 | #endif 50 | 51 | 52 | #if SD_MAC 53 | #import 54 | #ifndef UIImage 55 | #define UIImage NSImage 56 | #endif 57 | #ifndef UIImageView 58 | #define UIImageView NSImageView 59 | #endif 60 | #ifndef UIView 61 | #define UIView NSView 62 | #endif 63 | #else 64 | #if __IPHONE_OS_VERSION_MIN_REQUIRED != 20000 && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0 65 | #error SDWebImage doesn't support Deployment Target version < 5.0 66 | #endif 67 | 68 | #if SD_UIKIT 69 | #import 70 | #endif 71 | #if SD_WATCH 72 | #import 73 | #endif 74 | #endif 75 | 76 | #ifndef NS_ENUM 77 | #define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type 78 | #endif 79 | 80 | #ifndef NS_OPTIONS 81 | #define NS_OPTIONS(_type, _name) enum _name : _type _name; enum _name : _type 82 | #endif 83 | 84 | #if OS_OBJECT_USE_OBJC 85 | #undef SDDispatchQueueRelease 86 | #undef SDDispatchQueueSetterSementics 87 | #define SDDispatchQueueRelease(q) 88 | #define SDDispatchQueueSetterSementics strong 89 | #else 90 | #undef SDDispatchQueueRelease 91 | #undef SDDispatchQueueSetterSementics 92 | #define SDDispatchQueueRelease(q) (dispatch_release(q)) 93 | #define SDDispatchQueueSetterSementics assign 94 | #endif 95 | 96 | extern UIImage *SDScaledImageForKey(NSString *key, UIImage *image); 97 | 98 | typedef void(^SDWebImageNoParamsBlock)(); 99 | 100 | extern NSString *const SDWebImageErrorDomain; 101 | 102 | #ifndef dispatch_main_async_safe 103 | #define dispatch_main_async_safe(block)\ 104 | if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {\ 105 | block();\ 106 | } else {\ 107 | dispatch_async(dispatch_get_main_queue(), block);\ 108 | } 109 | #endif 110 | 111 | static int64_t kAsyncTestTimeout = 5; 112 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Utils/SDWebImagePrefetcher.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import 10 | #import "SDWebImageManager.h" 11 | 12 | @class SDWebImagePrefetcher; 13 | 14 | @protocol SDWebImagePrefetcherDelegate 15 | 16 | @optional 17 | 18 | /** 19 | * Called when an image was prefetched. 20 | * 21 | * @param imagePrefetcher The current image prefetcher 22 | * @param imageURL The image url that was prefetched 23 | * @param finishedCount The total number of images that were prefetched (successful or not) 24 | * @param totalCount The total number of images that were to be prefetched 25 | */ 26 | - (void)imagePrefetcher:(nonnull SDWebImagePrefetcher *)imagePrefetcher didPrefetchURL:(nullable NSURL *)imageURL finishedCount:(NSUInteger)finishedCount totalCount:(NSUInteger)totalCount; 27 | 28 | /** 29 | * Called when all images are prefetched. 30 | * @param imagePrefetcher The current image prefetcher 31 | * @param totalCount The total number of images that were prefetched (whether successful or not) 32 | * @param skippedCount The total number of images that were skipped 33 | */ 34 | - (void)imagePrefetcher:(nonnull SDWebImagePrefetcher *)imagePrefetcher didFinishWithTotalCount:(NSUInteger)totalCount skippedCount:(NSUInteger)skippedCount; 35 | 36 | @end 37 | 38 | typedef void(^SDWebImagePrefetcherProgressBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfTotalUrls); 39 | typedef void(^SDWebImagePrefetcherCompletionBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfSkippedUrls); 40 | 41 | /** 42 | * Prefetch some URLs in the cache for future use. Images are downloaded in low priority. 43 | */ 44 | @interface SDWebImagePrefetcher : NSObject 45 | 46 | /** 47 | * The web image manager 48 | */ 49 | @property (strong, nonatomic, readonly, nonnull) SDWebImageManager *manager; 50 | 51 | /** 52 | * Maximum number of URLs to prefetch at the same time. Defaults to 3. 53 | */ 54 | @property (nonatomic, assign) NSUInteger maxConcurrentDownloads; 55 | 56 | /** 57 | * SDWebImageOptions for prefetcher. Defaults to SDWebImageLowPriority. 58 | */ 59 | @property (nonatomic, assign) SDWebImageOptions options; 60 | 61 | /** 62 | * Queue options for Prefetcher. Defaults to Main Queue. 63 | */ 64 | @property (nonatomic, assign, nonnull) dispatch_queue_t prefetcherQueue; 65 | 66 | @property (weak, nonatomic, nullable) id delegate; 67 | 68 | /** 69 | * Return the global image prefetcher instance. 70 | */ 71 | + (nonnull instancetype)sharedImagePrefetcher; 72 | 73 | /** 74 | * Allows you to instantiate a prefetcher with any arbitrary image manager. 75 | */ 76 | - (nonnull instancetype)initWithImageManager:(nonnull SDWebImageManager *)manager NS_DESIGNATED_INITIALIZER; 77 | 78 | /** 79 | * Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching, 80 | * currently one image is downloaded at a time, 81 | * and skips images for failed downloads and proceed to the next image in the list 82 | * 83 | * @param urls list of URLs to prefetch 84 | */ 85 | - (void)prefetchURLs:(nullable NSArray *)urls; 86 | 87 | /** 88 | * Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching, 89 | * currently one image is downloaded at a time, 90 | * and skips images for failed downloads and proceed to the next image in the list 91 | * 92 | * @param urls list of URLs to prefetch 93 | * @param progressBlock block to be called when progress updates; 94 | * first parameter is the number of completed (successful or not) requests, 95 | * second parameter is the total number of images originally requested to be prefetched 96 | * @param completionBlock block to be called when prefetching is completed 97 | * first param is the number of completed (successful or not) requests, 98 | * second parameter is the number of skipped requests 99 | */ 100 | - (void)prefetchURLs:(nullable NSArray *)urls 101 | progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock 102 | completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock; 103 | 104 | /** 105 | * Remove and cancel queued list 106 | */ 107 | - (void)cancelPrefetching; 108 | 109 | 110 | @end 111 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/UI_Categories/UIImageView+HighlightedWebCache.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDWebImageCompat.h" 10 | 11 | #if SD_UIKIT 12 | 13 | #import "SDWebImageManager.h" 14 | 15 | /** 16 | * Integrates SDWebImage async downloading and caching of remote images with UIImageView for highlighted state. 17 | */ 18 | @interface UIImageView (HighlightedWebCache) 19 | 20 | /** 21 | * Set the imageView `highlightedImage` with an `url`. 22 | * 23 | * The download is asynchronous and cached. 24 | * 25 | * @param url The url for the image. 26 | */ 27 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url; 28 | 29 | /** 30 | * Set the imageView `highlightedImage` with an `url` and custom options. 31 | * 32 | * The download is asynchronous and cached. 33 | * 34 | * @param url The url for the image. 35 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. 36 | */ 37 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url 38 | options:(SDWebImageOptions)options; 39 | 40 | /** 41 | * Set the imageView `highlightedImage` with an `url`. 42 | * 43 | * The download is asynchronous and cached. 44 | * 45 | * @param url The url for the image. 46 | * @param completedBlock A block called when operation has been completed. This block has no return value 47 | * and takes the requested UIImage as first parameter. In case of error the image parameter 48 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 49 | * indicating if the image was retrieved from the local cache or from the network. 50 | * The fourth parameter is the original image url. 51 | */ 52 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url 53 | completed:(nullable SDExternalCompletionBlock)completedBlock; 54 | 55 | /** 56 | * Set the imageView `highlightedImage` with an `url` and custom options. 57 | * 58 | * The download is asynchronous and cached. 59 | * 60 | * @param url The url for the image. 61 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. 62 | * @param completedBlock A block called when operation has been completed. This block has no return value 63 | * and takes the requested UIImage as first parameter. In case of error the image parameter 64 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 65 | * indicating if the image was retrieved from the local cache or from the network. 66 | * The fourth parameter is the original image url. 67 | */ 68 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url 69 | options:(SDWebImageOptions)options 70 | completed:(nullable SDExternalCompletionBlock)completedBlock; 71 | 72 | /** 73 | * Set the imageView `highlightedImage` with an `url` and custom options. 74 | * 75 | * The download is asynchronous and cached. 76 | * 77 | * @param url The url for the image. 78 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. 79 | * @param progressBlock A block called while image is downloading 80 | * @note the progress block is executed on a background queue 81 | * @param completedBlock A block called when operation has been completed. This block has no return value 82 | * and takes the requested UIImage as first parameter. In case of error the image parameter 83 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 84 | * indicating if the image was retrieved from the local cache or from the network. 85 | * The fourth parameter is the original image url. 86 | */ 87 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url 88 | options:(SDWebImageOptions)options 89 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 90 | completed:(nullable SDExternalCompletionBlock)completedBlock; 91 | 92 | @end 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/UI_Categories/MKAnnotationView+WebCache.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDWebImageCompat.h" 10 | 11 | #if SD_UIKIT || SD_MAC 12 | 13 | #import 14 | #import "SDWebImageManager.h" 15 | 16 | /** 17 | * Integrates SDWebImage async downloading and caching of remote images with MKAnnotationView. 18 | */ 19 | @interface MKAnnotationView (WebCache) 20 | 21 | /** 22 | * Set the imageView `image` with an `url`. 23 | * 24 | * The download is asynchronous and cached. 25 | * 26 | * @param url The url for the image. 27 | */ 28 | - (void)sd_setImageWithURL:(nullable NSURL *)url; 29 | 30 | /** 31 | * Set the imageView `image` with an `url` and a placeholder. 32 | * 33 | * The download is asynchronous and cached. 34 | * 35 | * @param url The url for the image. 36 | * @param placeholder The image to be set initially, until the image request finishes. 37 | * @see sd_setImageWithURL:placeholderImage:options: 38 | */ 39 | - (void)sd_setImageWithURL:(nullable NSURL *)url 40 | placeholderImage:(nullable UIImage *)placeholder; 41 | 42 | /** 43 | * Set the imageView `image` with an `url`, placeholder and custom options. 44 | * 45 | * The download is asynchronous and cached. 46 | * 47 | * @param url The url for the image. 48 | * @param placeholder The image to be set initially, until the image request finishes. 49 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. 50 | */ 51 | 52 | - (void)sd_setImageWithURL:(nullable NSURL *)url 53 | placeholderImage:(nullable UIImage *)placeholder 54 | options:(SDWebImageOptions)options; 55 | 56 | /** 57 | * Set the imageView `image` with an `url`. 58 | * 59 | * The download is asynchronous and cached. 60 | * 61 | * @param url The url for the image. 62 | * @param completedBlock A block called when operation has been completed. This block has no return value 63 | * and takes the requested UIImage as first parameter. In case of error the image parameter 64 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 65 | * indicating if the image was retrieved from the local cache or from the network. 66 | * The fourth parameter is the original image url. 67 | */ 68 | - (void)sd_setImageWithURL:(nullable NSURL *)url 69 | completed:(nullable SDExternalCompletionBlock)completedBlock; 70 | 71 | /** 72 | * Set the imageView `image` with an `url`, placeholder. 73 | * 74 | * The download is asynchronous and cached. 75 | * 76 | * @param url The url for the image. 77 | * @param placeholder The image to be set initially, until the image request finishes. 78 | * @param completedBlock A block called when operation has been completed. This block has no return value 79 | * and takes the requested UIImage as first parameter. In case of error the image parameter 80 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 81 | * indicating if the image was retrieved from the local cache or from the network. 82 | * The fourth parameter is the original image url. 83 | */ 84 | - (void)sd_setImageWithURL:(nullable NSURL *)url 85 | placeholderImage:(nullable UIImage *)placeholder 86 | completed:(nullable SDExternalCompletionBlock)completedBlock; 87 | 88 | /** 89 | * Set the imageView `image` with an `url`, placeholder and custom options. 90 | * 91 | * The download is asynchronous and cached. 92 | * 93 | * @param url The url for the image. 94 | * @param placeholder The image to be set initially, until the image request finishes. 95 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. 96 | * @param completedBlock A block called when operation has been completed. This block has no return value 97 | * and takes the requested UIImage as first parameter. In case of error the image parameter 98 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 99 | * indicating if the image was retrieved from the local cache or from the network. 100 | * The fourth parameter is the original image url. 101 | */ 102 | - (void)sd_setImageWithURL:(nullable NSURL *)url 103 | placeholderImage:(nullable UIImage *)placeholder 104 | options:(SDWebImageOptions)options 105 | completed:(nullable SDExternalCompletionBlock)completedBlock; 106 | 107 | @end 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Downloader/SDWebImageDownloaderOperation.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import 10 | #import "SDWebImageDownloader.h" 11 | #import "SDWebImageOperation.h" 12 | 13 | extern NSString * _Nonnull const SDWebImageDownloadStartNotification; 14 | extern NSString * _Nonnull const SDWebImageDownloadReceiveResponseNotification; 15 | extern NSString * _Nonnull const SDWebImageDownloadStopNotification; 16 | extern NSString * _Nonnull const SDWebImageDownloadFinishNotification; 17 | 18 | 19 | 20 | /** 21 | Describes a downloader operation. If one wants to use a custom downloader op, it needs to inherit from `NSOperation` and conform to this protocol 22 | */ 23 | @protocol SDWebImageDownloaderOperationInterface 24 | 25 | - (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request 26 | inSession:(nullable NSURLSession *)session 27 | options:(SDWebImageDownloaderOptions)options; 28 | 29 | - (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 30 | completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock; 31 | 32 | - (BOOL)shouldDecompressImages; 33 | - (void)setShouldDecompressImages:(BOOL)value; 34 | 35 | - (nullable NSURLCredential *)credential; 36 | - (void)setCredential:(nullable NSURLCredential *)value; 37 | 38 | @end 39 | 40 | 41 | @interface SDWebImageDownloaderOperation : NSOperation 42 | 43 | /** 44 | * The request used by the operation's task. 45 | */ 46 | @property (strong, nonatomic, readonly, nullable) NSURLRequest *request; 47 | 48 | /** 49 | * The operation's task 50 | */ 51 | @property (strong, nonatomic, readonly, nullable) NSURLSessionTask *dataTask; 52 | 53 | 54 | @property (assign, nonatomic) BOOL shouldDecompressImages; 55 | 56 | /** 57 | * Was used to determine whether the URL connection should consult the credential storage for authenticating the connection. 58 | * @deprecated Not used for a couple of versions 59 | */ 60 | @property (nonatomic, assign) BOOL shouldUseCredentialStorage __deprecated_msg("Property deprecated. Does nothing. Kept only for backwards compatibility"); 61 | 62 | /** 63 | * The credential used for authentication challenges in `-connection:didReceiveAuthenticationChallenge:`. 64 | * 65 | * This will be overridden by any shared credentials that exist for the username or password of the request URL, if present. 66 | */ 67 | @property (nonatomic, strong, nullable) NSURLCredential *credential; 68 | 69 | /** 70 | * The SDWebImageDownloaderOptions for the receiver. 71 | */ 72 | @property (assign, nonatomic, readonly) SDWebImageDownloaderOptions options; 73 | 74 | /** 75 | * The expected size of data. 76 | */ 77 | @property (assign, nonatomic) NSInteger expectedSize; 78 | 79 | /** 80 | * The response returned by the operation's connection. 81 | */ 82 | @property (strong, nonatomic, nullable) NSURLResponse *response; 83 | 84 | /** 85 | * Initializes a `SDWebImageDownloaderOperation` object 86 | * 87 | * @see SDWebImageDownloaderOperation 88 | * 89 | * @param request the URL request 90 | * @param session the URL session in which this operation will run 91 | * @param options downloader options 92 | * 93 | * @return the initialized instance 94 | */ 95 | - (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request 96 | inSession:(nullable NSURLSession *)session 97 | options:(SDWebImageDownloaderOptions)options NS_DESIGNATED_INITIALIZER; 98 | 99 | /** 100 | * Adds handlers for progress and completion. Returns a tokent that can be passed to -cancel: to cancel this set of 101 | * callbacks. 102 | * 103 | * @param progressBlock the block executed when a new chunk of data arrives. 104 | * @note the progress block is executed on a background queue 105 | * @param completedBlock the block executed when the download is done. 106 | * @note the completed block is executed on the main queue for success. If errors are found, there is a chance the block will be executed on a background queue 107 | * 108 | * @return the token to use to cancel this set of handlers 109 | */ 110 | - (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 111 | completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock; 112 | 113 | /** 114 | * Cancels a set of callbacks. Once all callbacks are canceled, the operation is cancelled. 115 | * 116 | * @param token the token representing a set of callbacks to cancel 117 | * 118 | * @return YES if the operation was stopped because this was the last token to be canceled. NO otherwise. 119 | */ 120 | - (BOOL)cancel:(nullable id)token; 121 | 122 | @end 123 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/UI_Categories/UIImageView+WebCache.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "UIImageView+WebCache.h" 10 | 11 | #if SD_UIKIT || SD_MAC 12 | 13 | #import "objc/runtime.h" 14 | #import "UIView+WebCacheOperation.h" 15 | #import "UIView+WebCache.h" 16 | 17 | @implementation UIImageView (WebCache) 18 | 19 | - (void)sd_setImageWithURL:(nullable NSURL *)url { 20 | [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil]; 21 | } 22 | 23 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder { 24 | [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil]; 25 | } 26 | 27 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options { 28 | [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil]; 29 | } 30 | 31 | - (void)sd_setImageWithURL:(nullable NSURL *)url completed:(nullable SDExternalCompletionBlock)completedBlock { 32 | [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:completedBlock]; 33 | } 34 | 35 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock { 36 | [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:completedBlock]; 37 | } 38 | 39 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options completed:(nullable SDExternalCompletionBlock)completedBlock { 40 | [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:completedBlock]; 41 | } 42 | 43 | - (void)sd_setImageWithURL:(nullable NSURL *)url 44 | placeholderImage:(nullable UIImage *)placeholder 45 | options:(SDWebImageOptions)options 46 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 47 | completed:(nullable SDExternalCompletionBlock)completedBlock { 48 | [self sd_internalSetImageWithURL:url 49 | placeholderImage:placeholder 50 | options:options 51 | operationKey:nil 52 | setImageBlock:nil 53 | progress:progressBlock 54 | completed:completedBlock]; 55 | } 56 | 57 | - (void)sd_setImageWithPreviousCachedImageWithURL:(nullable NSURL *)url 58 | placeholderImage:(nullable UIImage *)placeholder 59 | options:(SDWebImageOptions)options 60 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 61 | completed:(nullable SDExternalCompletionBlock)completedBlock { 62 | NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:url]; 63 | UIImage *lastPreviousCachedImage = [[SDImageCache sharedImageCache] imageFromCacheForKey:key]; 64 | 65 | [self sd_setImageWithURL:url placeholderImage:lastPreviousCachedImage ?: placeholder options:options progress:progressBlock completed:completedBlock]; 66 | } 67 | 68 | #if SD_UIKIT 69 | 70 | #pragma mark - Animation of multiple images 71 | 72 | - (void)sd_setAnimationImagesWithURLs:(nonnull NSArray *)arrayOfURLs { 73 | [self sd_cancelCurrentAnimationImagesLoad]; 74 | __weak __typeof(self)wself = self; 75 | 76 | NSMutableArray> *operationsArray = [[NSMutableArray alloc] init]; 77 | 78 | for (NSURL *logoImageURL in arrayOfURLs) { 79 | id operation = [SDWebImageManager.sharedManager loadImageWithURL:logoImageURL options:0 progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { 80 | if (!wself) return; 81 | dispatch_main_async_safe(^{ 82 | __strong UIImageView *sself = wself; 83 | [sself stopAnimating]; 84 | if (sself && image) { 85 | NSMutableArray *currentImages = [[sself animationImages] mutableCopy]; 86 | if (!currentImages) { 87 | currentImages = [[NSMutableArray alloc] init]; 88 | } 89 | [currentImages addObject:image]; 90 | 91 | sself.animationImages = currentImages; 92 | [sself setNeedsLayout]; 93 | } 94 | [sself startAnimating]; 95 | }); 96 | }]; 97 | [operationsArray addObject:operation]; 98 | } 99 | 100 | [self sd_setImageLoadOperation:[operationsArray copy] forKey:@"UIImageViewAnimationImages"]; 101 | } 102 | 103 | - (void)sd_cancelCurrentAnimationImagesLoad { 104 | [self sd_cancelImageLoadOperationWithKey:@"UIImageViewAnimationImages"]; 105 | } 106 | #endif 107 | 108 | @end 109 | 110 | #endif 111 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Utils/SDWebImagePrefetcher.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDWebImagePrefetcher.h" 10 | 11 | @interface SDWebImagePrefetcher () 12 | 13 | @property (strong, nonatomic, nonnull) SDWebImageManager *manager; 14 | @property (strong, nonatomic, nullable) NSArray *prefetchURLs; 15 | @property (assign, nonatomic) NSUInteger requestedCount; 16 | @property (assign, nonatomic) NSUInteger skippedCount; 17 | @property (assign, nonatomic) NSUInteger finishedCount; 18 | @property (assign, nonatomic) NSTimeInterval startedTime; 19 | @property (copy, nonatomic, nullable) SDWebImagePrefetcherCompletionBlock completionBlock; 20 | @property (copy, nonatomic, nullable) SDWebImagePrefetcherProgressBlock progressBlock; 21 | 22 | @end 23 | 24 | @implementation SDWebImagePrefetcher 25 | 26 | + (nonnull instancetype)sharedImagePrefetcher { 27 | static dispatch_once_t once; 28 | static id instance; 29 | dispatch_once(&once, ^{ 30 | instance = [self new]; 31 | }); 32 | return instance; 33 | } 34 | 35 | - (nonnull instancetype)init { 36 | return [self initWithImageManager:[SDWebImageManager new]]; 37 | } 38 | 39 | - (nonnull instancetype)initWithImageManager:(SDWebImageManager *)manager { 40 | if ((self = [super init])) { 41 | _manager = manager; 42 | _options = SDWebImageLowPriority; 43 | _prefetcherQueue = dispatch_get_main_queue(); 44 | self.maxConcurrentDownloads = 3; 45 | } 46 | return self; 47 | } 48 | 49 | - (void)setMaxConcurrentDownloads:(NSUInteger)maxConcurrentDownloads { 50 | self.manager.imageDownloader.maxConcurrentDownloads = maxConcurrentDownloads; 51 | } 52 | 53 | - (NSUInteger)maxConcurrentDownloads { 54 | return self.manager.imageDownloader.maxConcurrentDownloads; 55 | } 56 | 57 | - (void)startPrefetchingAtIndex:(NSUInteger)index { 58 | if (index >= self.prefetchURLs.count) return; 59 | self.requestedCount++; 60 | [self.manager loadImageWithURL:self.prefetchURLs[index] options:self.options progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { 61 | if (!finished) return; 62 | self.finishedCount++; 63 | 64 | if (image) { 65 | if (self.progressBlock) { 66 | self.progressBlock(self.finishedCount,(self.prefetchURLs).count); 67 | } 68 | } 69 | else { 70 | if (self.progressBlock) { 71 | self.progressBlock(self.finishedCount,(self.prefetchURLs).count); 72 | } 73 | // Add last failed 74 | self.skippedCount++; 75 | } 76 | if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didPrefetchURL:finishedCount:totalCount:)]) { 77 | [self.delegate imagePrefetcher:self 78 | didPrefetchURL:self.prefetchURLs[index] 79 | finishedCount:self.finishedCount 80 | totalCount:self.prefetchURLs.count 81 | ]; 82 | } 83 | if (self.prefetchURLs.count > self.requestedCount) { 84 | dispatch_async(self.prefetcherQueue, ^{ 85 | [self startPrefetchingAtIndex:self.requestedCount]; 86 | }); 87 | } else if (self.finishedCount == self.requestedCount) { 88 | [self reportStatus]; 89 | if (self.completionBlock) { 90 | self.completionBlock(self.finishedCount, self.skippedCount); 91 | self.completionBlock = nil; 92 | } 93 | self.progressBlock = nil; 94 | } 95 | }]; 96 | } 97 | 98 | - (void)reportStatus { 99 | NSUInteger total = (self.prefetchURLs).count; 100 | if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didFinishWithTotalCount:skippedCount:)]) { 101 | [self.delegate imagePrefetcher:self 102 | didFinishWithTotalCount:(total - self.skippedCount) 103 | skippedCount:self.skippedCount 104 | ]; 105 | } 106 | } 107 | 108 | - (void)prefetchURLs:(nullable NSArray *)urls { 109 | [self prefetchURLs:urls progress:nil completed:nil]; 110 | } 111 | 112 | - (void)prefetchURLs:(nullable NSArray *)urls 113 | progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock 114 | completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock { 115 | [self cancelPrefetching]; // Prevent duplicate prefetch request 116 | self.startedTime = CFAbsoluteTimeGetCurrent(); 117 | self.prefetchURLs = urls; 118 | self.completionBlock = completionBlock; 119 | self.progressBlock = progressBlock; 120 | 121 | if (urls.count == 0) { 122 | if (completionBlock) { 123 | completionBlock(0,0); 124 | } 125 | } else { 126 | // Starts prefetching from the very first image on the list with the max allowed concurrency 127 | NSUInteger listCount = self.prefetchURLs.count; 128 | for (NSUInteger i = 0; i < self.maxConcurrentDownloads && self.requestedCount < listCount; i++) { 129 | [self startPrefetchingAtIndex:i]; 130 | } 131 | } 132 | } 133 | 134 | - (void)cancelPrefetching { 135 | self.prefetchURLs = nil; 136 | self.skippedCount = 0; 137 | self.requestedCount = 0; 138 | self.finishedCount = 0; 139 | [self.manager cancelAll]; 140 | } 141 | 142 | @end 143 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Categories/UIImage+MultiFormat.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "UIImage+MultiFormat.h" 10 | #import "UIImage+GIF.h" 11 | #import "NSData+ImageContentType.h" 12 | #import 13 | 14 | #ifdef SD_WEBP 15 | #import "UIImage+WebP.h" 16 | #endif 17 | 18 | @implementation UIImage (MultiFormat) 19 | 20 | + (nullable UIImage *)sd_imageWithData:(nullable NSData *)data { 21 | if (!data) { 22 | return nil; 23 | } 24 | 25 | UIImage *image; 26 | //识别图片格式 27 | SDImageFormat imageFormat = [NSData sd_imageFormatForImageData:data]; 28 | if (imageFormat == SDImageFormatGIF) { 29 | // gif图片处理,得到静态图片 30 | image = [UIImage sd_animatedGIFWithData:data]; 31 | } 32 | //是否开启了 WEBP 格式支持 33 | #ifdef SD_WEBP 34 | // webp图片处理 35 | else if (imageFormat == SDImageFormatWebP) 36 | { 37 | // 得到webp格式图片 38 | image = [UIImage sd_imageWithWebPData:data]; 39 | } 40 | #endif 41 | // 静态图片处理 42 | else { 43 | image = [[UIImage alloc] initWithData:data]; 44 | #if SD_UIKIT || SD_WATCH 45 | //图片方向处理 46 | UIImageOrientation orientation = [self sd_imageOrientationFromImageData:data]; 47 | // 图片的方向默认是UIImageOrientationUp,若是本来图片的方向就是UIImageOrientationUp那么就不需要重新处理图片的方向 48 | if (orientation != UIImageOrientationUp) { 49 | image = [UIImage imageWithCGImage:image.CGImage 50 | scale:image.scale 51 | orientation:orientation]; 52 | } 53 | #endif 54 | } 55 | 56 | 57 | return image; 58 | } 59 | 60 | #if SD_UIKIT || SD_WATCH 61 | //获取图片的方向 62 | +(UIImageOrientation)sd_imageOrientationFromImageData:(nonnull NSData *)imageData { 63 | UIImageOrientation result = UIImageOrientationUp; 64 | CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL); 65 | if (imageSource) { 66 | CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL); 67 | if (properties) { 68 | CFTypeRef val; 69 | int exifOrientation; 70 | val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation); 71 | if (val) { 72 | CFNumberGetValue(val, kCFNumberIntType, &exifOrientation); 73 | result = [self sd_exifOrientationToiOSOrientation:exifOrientation]; 74 | } // else - if it's not set it remains at up 75 | CFRelease((CFTypeRef) properties); 76 | } else { 77 | //NSLog(@"NO PROPERTIES, FAIL"); 78 | } 79 | CFRelease(imageSource); 80 | } 81 | return result; 82 | } 83 | 84 | #pragma mark EXIF orientation tag converter 85 | // Convert an EXIF image orientation to an iOS one. 86 | // reference see here: http://sylvana.net/jpegcrop/exif_orientation.html 87 | // 转化图片方向 88 | + (UIImageOrientation) sd_exifOrientationToiOSOrientation:(int)exifOrientation { 89 | UIImageOrientation orientation = UIImageOrientationUp; 90 | switch (exifOrientation) { 91 | case 1: 92 | orientation = UIImageOrientationUp; 93 | break; 94 | 95 | case 3: 96 | orientation = UIImageOrientationDown; 97 | break; 98 | 99 | case 8: 100 | orientation = UIImageOrientationLeft; 101 | break; 102 | 103 | case 6: 104 | orientation = UIImageOrientationRight; 105 | break; 106 | 107 | case 2: 108 | orientation = UIImageOrientationUpMirrored; 109 | break; 110 | 111 | case 4: 112 | orientation = UIImageOrientationDownMirrored; 113 | break; 114 | 115 | case 5: 116 | orientation = UIImageOrientationLeftMirrored; 117 | break; 118 | 119 | case 7: 120 | orientation = UIImageOrientationRightMirrored; 121 | break; 122 | default: 123 | break; 124 | } 125 | return orientation; 126 | } 127 | #endif 128 | 129 | - (nullable NSData *)sd_imageData { 130 | return [self sd_imageDataAsFormat:SDImageFormatUndefined]; 131 | } 132 | 133 | - (nullable NSData *)sd_imageDataAsFormat:(SDImageFormat)imageFormat { 134 | NSData *imageData = nil; 135 | if (self) { 136 | #if SD_UIKIT || SD_WATCH 137 | int alphaInfo = CGImageGetAlphaInfo(self.CGImage); 138 | BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone || 139 | alphaInfo == kCGImageAlphaNoneSkipFirst || 140 | alphaInfo == kCGImageAlphaNoneSkipLast); 141 | 142 | BOOL usePNG = hasAlpha; 143 | 144 | // the imageFormat param has priority here. But if the format is undefined, we relly on the alpha channel 145 | if (imageFormat != SDImageFormatUndefined) { 146 | usePNG = (imageFormat == SDImageFormatPNG); 147 | } 148 | 149 | if (usePNG) { 150 | imageData = UIImagePNGRepresentation(self); 151 | } else { 152 | imageData = UIImageJPEGRepresentation(self, (CGFloat)1.0); 153 | } 154 | #else 155 | NSBitmapImageFileType imageFileType = NSJPEGFileType; 156 | if (imageFormat == SDImageFormatGIF) { 157 | imageFileType = NSGIFFileType; 158 | } else if (imageFormat == SDImageFormatPNG) { 159 | imageFileType = NSPNGFileType; 160 | } 161 | 162 | imageData = [NSBitmapImageRep representationOfImageRepsInArray:self.representations 163 | usingType:imageFileType 164 | properties:@{}]; 165 | #endif 166 | } 167 | return imageData; 168 | } 169 | 170 | 171 | @end 172 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Categories/UIImage+WebP.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #ifdef SD_WEBP 10 | 11 | #import "UIImage+WebP.h" 12 | #import "webp/decode.h" 13 | #import "webp/mux_types.h" 14 | #import "webp/demux.h" 15 | #import "NSImage+WebCache.h" 16 | 17 | // Callback for CGDataProviderRelease 18 | static void FreeImageData(void *info, const void *data, size_t size) { 19 | free((void *)data); 20 | } 21 | 22 | @implementation UIImage (WebP) 23 | 24 | + (nullable UIImage *)sd_imageWithWebPData:(nullable NSData *)data { 25 | if (!data) { 26 | return nil; 27 | } 28 | 29 | WebPData webpData; 30 | WebPDataInit(&webpData); 31 | webpData.bytes = data.bytes; 32 | webpData.size = data.length; 33 | WebPDemuxer *demuxer = WebPDemux(&webpData); 34 | if (!demuxer) { 35 | return nil; 36 | } 37 | 38 | uint32_t flags = WebPDemuxGetI(demuxer, WEBP_FF_FORMAT_FLAGS); 39 | if (!(flags & ANIMATION_FLAG)) { 40 | // for static single webp image 41 | UIImage *staticImage = [self sd_rawWepImageWithData:webpData]; 42 | WebPDemuxDelete(demuxer); 43 | return staticImage; 44 | } 45 | 46 | WebPIterator iter; 47 | if (!WebPDemuxGetFrame(demuxer, 1, &iter)) { 48 | WebPDemuxReleaseIterator(&iter); 49 | WebPDemuxDelete(demuxer); 50 | return nil; 51 | } 52 | 53 | NSMutableArray *images = [NSMutableArray array]; 54 | NSTimeInterval duration = 0; 55 | 56 | do { 57 | UIImage *image; 58 | if (iter.blend_method == WEBP_MUX_BLEND) { 59 | image = [self sd_blendWebpImageWithOriginImage:[images lastObject] iterator:iter]; 60 | } else { 61 | image = [self sd_rawWepImageWithData:iter.fragment]; 62 | } 63 | 64 | if (!image) { 65 | continue; 66 | } 67 | 68 | [images addObject:image]; 69 | duration += iter.duration / 1000.0f; 70 | 71 | } while (WebPDemuxNextFrame(&iter)); 72 | 73 | WebPDemuxReleaseIterator(&iter); 74 | WebPDemuxDelete(demuxer); 75 | 76 | UIImage *finalImage = nil; 77 | #if SD_UIKIT || SD_WATCH 78 | finalImage = [UIImage animatedImageWithImages:images duration:duration]; 79 | #elif SD_MAC 80 | if ([images count] > 0) { 81 | finalImage = images[0]; 82 | } 83 | #endif 84 | return finalImage; 85 | } 86 | 87 | 88 | + (nullable UIImage *)sd_blendWebpImageWithOriginImage:(nullable UIImage *)originImage iterator:(WebPIterator)iter { 89 | if (!originImage) { 90 | return nil; 91 | } 92 | 93 | CGSize size = originImage.size; 94 | CGFloat tmpX = iter.x_offset; 95 | CGFloat tmpY = size.height - iter.height - iter.y_offset; 96 | CGRect imageRect = CGRectMake(tmpX, tmpY, iter.width, iter.height); 97 | 98 | UIImage *image = [self sd_rawWepImageWithData:iter.fragment]; 99 | if (!image) { 100 | return nil; 101 | } 102 | 103 | CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); 104 | uint32_t bitmapInfo = iter.has_alpha ? kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast : 0; 105 | CGContextRef blendCanvas = CGBitmapContextCreate(NULL, size.width, size.height, 8, 0, colorSpaceRef, bitmapInfo); 106 | CGContextDrawImage(blendCanvas, CGRectMake(0, 0, size.width, size.height), originImage.CGImage); 107 | CGContextDrawImage(blendCanvas, imageRect, image.CGImage); 108 | CGImageRef newImageRef = CGBitmapContextCreateImage(blendCanvas); 109 | 110 | #if SD_UIKIT || SD_WATCH 111 | image = [UIImage imageWithCGImage:newImageRef]; 112 | #elif SD_MAC 113 | image = [[UIImage alloc] initWithCGImage:newImageRef size:NSZeroSize]; 114 | #endif 115 | 116 | CGImageRelease(newImageRef); 117 | CGContextRelease(blendCanvas); 118 | CGColorSpaceRelease(colorSpaceRef); 119 | 120 | return image; 121 | } 122 | 123 | + (nullable UIImage *)sd_rawWepImageWithData:(WebPData)webpData { 124 | WebPDecoderConfig config; 125 | if (!WebPInitDecoderConfig(&config)) { 126 | return nil; 127 | } 128 | 129 | if (WebPGetFeatures(webpData.bytes, webpData.size, &config.input) != VP8_STATUS_OK) { 130 | return nil; 131 | } 132 | 133 | config.output.colorspace = config.input.has_alpha ? MODE_rgbA : MODE_RGB; 134 | config.options.use_threads = 1; 135 | 136 | // Decode the WebP image data into a RGBA value array. 137 | if (WebPDecode(webpData.bytes, webpData.size, &config) != VP8_STATUS_OK) { 138 | return nil; 139 | } 140 | 141 | int width = config.input.width; 142 | int height = config.input.height; 143 | if (config.options.use_scaling) { 144 | width = config.options.scaled_width; 145 | height = config.options.scaled_height; 146 | } 147 | 148 | // Construct a UIImage from the decoded RGBA value array. 149 | CGDataProviderRef provider = 150 | CGDataProviderCreateWithData(NULL, config.output.u.RGBA.rgba, config.output.u.RGBA.size, FreeImageData); 151 | CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); 152 | CGBitmapInfo bitmapInfo = config.input.has_alpha ? kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast : 0; 153 | size_t components = config.input.has_alpha ? 4 : 3; 154 | CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault; 155 | CGImageRef imageRef = CGImageCreate(width, height, 8, components * 8, components * width, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent); 156 | 157 | CGColorSpaceRelease(colorSpaceRef); 158 | CGDataProviderRelease(provider); 159 | 160 | #if SD_UIKIT || SD_WATCH 161 | UIImage *image = [[UIImage alloc] initWithCGImage:imageRef]; 162 | #else 163 | UIImage *image = [[UIImage alloc] initWithCGImage:imageRef size:NSZeroSize]; 164 | #endif 165 | CGImageRelease(imageRef); 166 | 167 | return image; 168 | } 169 | 170 | @end 171 | 172 | #endif 173 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/UI_Categories/UIButton+WebCache.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "UIButton+WebCache.h" 10 | 11 | #if SD_UIKIT 12 | 13 | #import "objc/runtime.h" 14 | #import "UIView+WebCacheOperation.h" 15 | #import "UIView+WebCache.h" 16 | 17 | static char imageURLStorageKey; 18 | 19 | typedef NSMutableDictionary SDStateImageURLDictionary; 20 | 21 | @implementation UIButton (WebCache) 22 | 23 | - (nullable NSURL *)sd_currentImageURL { 24 | NSURL *url = self.imageURLStorage[@(self.state)]; 25 | 26 | if (!url) { 27 | url = self.imageURLStorage[@(UIControlStateNormal)]; 28 | } 29 | 30 | return url; 31 | } 32 | 33 | - (nullable NSURL *)sd_imageURLForState:(UIControlState)state { 34 | return self.imageURLStorage[@(state)]; 35 | } 36 | 37 | #pragma mark - Image 38 | 39 | - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state { 40 | [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil]; 41 | } 42 | 43 | - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder { 44 | [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil]; 45 | } 46 | 47 | - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options { 48 | [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil]; 49 | } 50 | 51 | - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state completed:(nullable SDExternalCompletionBlock)completedBlock { 52 | [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:completedBlock]; 53 | } 54 | 55 | - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock { 56 | [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:completedBlock]; 57 | } 58 | 59 | - (void)sd_setImageWithURL:(nullable NSURL *)url 60 | forState:(UIControlState)state 61 | placeholderImage:(nullable UIImage *)placeholder 62 | options:(SDWebImageOptions)options 63 | completed:(nullable SDExternalCompletionBlock)completedBlock { 64 | if (!url) { 65 | [self.imageURLStorage removeObjectForKey:@(state)]; 66 | return; 67 | } 68 | 69 | self.imageURLStorage[@(state)] = url; 70 | 71 | __weak typeof(self)weakSelf = self; 72 | [self sd_internalSetImageWithURL:url 73 | placeholderImage:placeholder 74 | options:options 75 | operationKey:[NSString stringWithFormat:@"UIButtonImageOperation%@", @(state)] 76 | setImageBlock:^(UIImage *image, NSData *imageData) { 77 | [weakSelf setImage:image forState:state]; 78 | } 79 | progress:nil 80 | completed:completedBlock]; 81 | } 82 | 83 | #pragma mark - Background image 84 | 85 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state { 86 | [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil]; 87 | } 88 | 89 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder { 90 | [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil]; 91 | } 92 | 93 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options { 94 | [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil]; 95 | } 96 | 97 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state completed:(nullable SDExternalCompletionBlock)completedBlock { 98 | [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:completedBlock]; 99 | } 100 | 101 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock { 102 | [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:completedBlock]; 103 | } 104 | 105 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url 106 | forState:(UIControlState)state 107 | placeholderImage:(nullable UIImage *)placeholder 108 | options:(SDWebImageOptions)options 109 | completed:(nullable SDExternalCompletionBlock)completedBlock { 110 | if (!url) { 111 | [self.imageURLStorage removeObjectForKey:@(state)]; 112 | return; 113 | } 114 | 115 | self.imageURLStorage[@(state)] = url; 116 | 117 | __weak typeof(self)weakSelf = self; 118 | [self sd_internalSetImageWithURL:url 119 | placeholderImage:placeholder 120 | options:options 121 | operationKey:[NSString stringWithFormat:@"UIButtonBackgroundImageOperation%@", @(state)] 122 | setImageBlock:^(UIImage *image, NSData *imageData) { 123 | [weakSelf setBackgroundImage:image forState:state]; 124 | } 125 | progress:nil 126 | completed:completedBlock]; 127 | } 128 | 129 | - (void)sd_setImageLoadOperation:(id)operation forState:(UIControlState)state { 130 | [self sd_setImageLoadOperation:operation forKey:[NSString stringWithFormat:@"UIButtonImageOperation%@", @(state)]]; 131 | } 132 | 133 | - (void)sd_cancelImageLoadForState:(UIControlState)state { 134 | [self sd_cancelImageLoadOperationWithKey:[NSString stringWithFormat:@"UIButtonImageOperation%@", @(state)]]; 135 | } 136 | 137 | - (void)sd_setBackgroundImageLoadOperation:(id)operation forState:(UIControlState)state { 138 | [self sd_setImageLoadOperation:operation forKey:[NSString stringWithFormat:@"UIButtonBackgroundImageOperation%@", @(state)]]; 139 | } 140 | 141 | - (void)sd_cancelBackgroundImageLoadForState:(UIControlState)state { 142 | [self sd_cancelImageLoadOperationWithKey:[NSString stringWithFormat:@"UIButtonBackgroundImageOperation%@", @(state)]]; 143 | } 144 | 145 | - (SDStateImageURLDictionary *)imageURLStorage { 146 | SDStateImageURLDictionary *storage = objc_getAssociatedObject(self, &imageURLStorageKey); 147 | if (!storage) { 148 | storage = [NSMutableDictionary dictionary]; 149 | objc_setAssociatedObject(self, &imageURLStorageKey, storage, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 150 | } 151 | 152 | return storage; 153 | } 154 | 155 | @end 156 | 157 | #endif 158 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/UI_Categories/UIView+WebCache.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "UIView+WebCache.h" 10 | 11 | #if SD_UIKIT || SD_MAC 12 | 13 | #import "objc/runtime.h" 14 | #import "UIView+WebCacheOperation.h" 15 | 16 | static char imageURLKey; 17 | 18 | #if SD_UIKIT 19 | static char TAG_ACTIVITY_INDICATOR; 20 | static char TAG_ACTIVITY_STYLE; 21 | #endif 22 | static char TAG_ACTIVITY_SHOW; 23 | 24 | @implementation UIView (WebCache) 25 | 26 | - (nullable NSURL *)sd_imageURL { 27 | return objc_getAssociatedObject(self, &imageURLKey); 28 | } 29 | 30 | // 所有加载图片操作都调用这个方法 31 | - (void)sd_internalSetImageWithURL:(nullable NSURL *)url 32 | placeholderImage:(nullable UIImage *)placeholder 33 | options:(SDWebImageOptions)options 34 | operationKey:(nullable NSString *)operationKey 35 | setImageBlock:(nullable SDSetImageBlock)setImageBlock 36 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 37 | completed:(nullable SDExternalCompletionBlock)completedBlock { 38 | NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]); 39 | [self sd_cancelImageLoadOperationWithKey:validOperationKey]; //取消之前的下载任务 40 | objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC); //利用runtime动态添加属性 41 | 42 | if (!(options & SDWebImageDelayPlaceholder)) { 43 | //如果模式不是 SDWebImageDelayPlaceholder, 则先设置占位图 44 | dispatch_main_async_safe(^{ 45 | [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock]; 46 | }); 47 | } 48 | 49 | //url不为nil 50 | if (url) { 51 | // check if activityView is enabled or not 52 | if ([self sd_showActivityIndicatorView]) { 53 | [self sd_addActivityIndicator]; //根据设置判断是否显示进度条 54 | } 55 | 56 | __weak __typeof(self)wself = self; 57 | id operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { 58 | __strong __typeof (wself) sself = wself; 59 | [sself sd_removeActivityIndicator]; //移除进度条 60 | if (!sself) { 61 | return; //如果self不存在 及已经被释放则return 62 | } 63 | dispatch_main_async_safe(^{ 64 | if (!sself) { 65 | return; 66 | } 67 | if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) { 68 | // 设置的不自动设置图片, 则通过block返回image 69 | completedBlock(image, error, cacheType, url); 70 | return; 71 | } else if (image) { 72 | //设置图片 73 | [sself sd_setImage:image imageData:data basedOnClassOrViaCustomSetImageBlock:setImageBlock]; 74 | [sself sd_setNeedsLayout]; 75 | } else { 76 | if ((options & SDWebImageDelayPlaceholder)) { 77 | // image下载不成功则设置占位图 78 | [sself sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock]; 79 | [sself sd_setNeedsLayout]; 80 | } 81 | } 82 | if (completedBlock && finished) { 83 | completedBlock(image, error, cacheType, url); 84 | } 85 | }); 86 | }]; 87 | [self sd_setImageLoadOperation:operation forKey:validOperationKey]; //给添加的属性赋值 88 | } else { 89 | //url 为nil, block返回error 90 | dispatch_main_async_safe(^{ 91 | [self sd_removeActivityIndicator]; 92 | if (completedBlock) { 93 | NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}]; 94 | completedBlock(nil, error, SDImageCacheTypeNone, url); 95 | } 96 | }); 97 | } 98 | } 99 | 100 | - (void)sd_cancelCurrentImageLoad { 101 | [self sd_cancelImageLoadOperationWithKey:NSStringFromClass([self class])]; 102 | } 103 | 104 | - (void)sd_setImage:(UIImage *)image imageData:(NSData *)imageData basedOnClassOrViaCustomSetImageBlock:(SDSetImageBlock)setImageBlock { 105 | if (setImageBlock) { 106 | setImageBlock(image, imageData); 107 | return; 108 | } 109 | 110 | #if SD_UIKIT || SD_MAC 111 | if ([self isKindOfClass:[UIImageView class]]) { 112 | UIImageView *imageView = (UIImageView *)self; 113 | imageView.image = image; 114 | } 115 | #endif 116 | 117 | #if SD_UIKIT 118 | if ([self isKindOfClass:[UIButton class]]) { 119 | UIButton *button = (UIButton *)self; 120 | [button setImage:image forState:UIControlStateNormal]; 121 | } 122 | #endif 123 | } 124 | 125 | - (void)sd_setNeedsLayout { 126 | #if SD_UIKIT 127 | [self setNeedsLayout]; 128 | #elif SD_MAC 129 | [self setNeedsLayout:YES]; 130 | #endif 131 | } 132 | 133 | #pragma mark - Activity indicator 134 | 135 | #pragma mark - 136 | #if SD_UIKIT 137 | - (UIActivityIndicatorView *)activityIndicator { 138 | return (UIActivityIndicatorView *)objc_getAssociatedObject(self, &TAG_ACTIVITY_INDICATOR); 139 | } 140 | 141 | - (void)setActivityIndicator:(UIActivityIndicatorView *)activityIndicator { 142 | objc_setAssociatedObject(self, &TAG_ACTIVITY_INDICATOR, activityIndicator, OBJC_ASSOCIATION_RETAIN); 143 | } 144 | #endif 145 | 146 | - (void)sd_setShowActivityIndicatorView:(BOOL)show { 147 | objc_setAssociatedObject(self, &TAG_ACTIVITY_SHOW, @(show), OBJC_ASSOCIATION_RETAIN); 148 | } 149 | 150 | - (BOOL)sd_showActivityIndicatorView { 151 | return [objc_getAssociatedObject(self, &TAG_ACTIVITY_SHOW) boolValue]; 152 | } 153 | 154 | #if SD_UIKIT 155 | - (void)sd_setIndicatorStyle:(UIActivityIndicatorViewStyle)style{ 156 | objc_setAssociatedObject(self, &TAG_ACTIVITY_STYLE, [NSNumber numberWithInt:style], OBJC_ASSOCIATION_RETAIN); 157 | } 158 | 159 | - (int)sd_getIndicatorStyle{ 160 | return [objc_getAssociatedObject(self, &TAG_ACTIVITY_STYLE) intValue]; 161 | } 162 | #endif 163 | 164 | - (void)sd_addActivityIndicator { 165 | #if SD_UIKIT 166 | if (!self.activityIndicator) { 167 | self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:[self sd_getIndicatorStyle]]; 168 | self.activityIndicator.translatesAutoresizingMaskIntoConstraints = NO; 169 | 170 | dispatch_main_async_safe(^{ 171 | [self addSubview:self.activityIndicator]; 172 | 173 | [self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator 174 | attribute:NSLayoutAttributeCenterX 175 | relatedBy:NSLayoutRelationEqual 176 | toItem:self 177 | attribute:NSLayoutAttributeCenterX 178 | multiplier:1.0 179 | constant:0.0]]; 180 | [self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator 181 | attribute:NSLayoutAttributeCenterY 182 | relatedBy:NSLayoutRelationEqual 183 | toItem:self 184 | attribute:NSLayoutAttributeCenterY 185 | multiplier:1.0 186 | constant:0.0]]; 187 | }); 188 | } 189 | 190 | dispatch_main_async_safe(^{ 191 | [self.activityIndicator startAnimating]; 192 | }); 193 | #endif 194 | } 195 | 196 | - (void)sd_removeActivityIndicator { 197 | #if SD_UIKIT 198 | if (self.activityIndicator) { 199 | [self.activityIndicator removeFromSuperview]; 200 | self.activityIndicator = nil; 201 | } 202 | #endif 203 | } 204 | 205 | @end 206 | 207 | #endif 208 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/UI_Categories/UIImageView+WebCache.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDWebImageCompat.h" 10 | 11 | #if SD_UIKIT || SD_MAC 12 | 13 | #import "SDWebImageManager.h" 14 | 15 | /** 16 | * Integrates SDWebImage async downloading and caching of remote images with UIImageView. 17 | * 18 | * Usage with a UITableViewCell sub-class: 19 | * 20 | * @code 21 | 22 | #import 23 | 24 | ... 25 | 26 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 27 | { 28 | static NSString *MyIdentifier = @"MyIdentifier"; 29 | 30 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier]; 31 | 32 | if (cell == nil) { 33 | cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] 34 | autorelease]; 35 | } 36 | 37 | // Here we use the provided sd_setImageWithURL: method to load the web image 38 | // Ensure you use a placeholder image otherwise cells will be initialized with no image 39 | [cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://example.com/image.jpg"] 40 | placeholderImage:[UIImage imageNamed:@"placeholder"]]; 41 | 42 | cell.textLabel.text = @"My Text"; 43 | return cell; 44 | } 45 | 46 | * @endcode 47 | */ 48 | @interface UIImageView (WebCache) 49 | 50 | /** 51 | * Set the imageView `image` with an `url`. 52 | * 53 | * The download is asynchronous and cached. 54 | * 55 | * @param url The url for the image. 56 | */ 57 | - (void)sd_setImageWithURL:(nullable NSURL *)url; 58 | 59 | /** 60 | * Set the imageView `image` with an `url` and a placeholder. 61 | * 62 | * The download is asynchronous and cached. 63 | * 64 | * @param url The url for the image. 65 | * @param placeholder The image to be set initially, until the image request finishes. 66 | * @see sd_setImageWithURL:placeholderImage:options: 67 | */ 68 | - (void)sd_setImageWithURL:(nullable NSURL *)url 69 | placeholderImage:(nullable UIImage *)placeholder; 70 | 71 | /** 72 | * Set the imageView `image` with an `url`, placeholder and custom options. 73 | * 74 | * The download is asynchronous and cached. 75 | * 76 | * @param url The url for the image. 77 | * @param placeholder The image to be set initially, until the image request finishes. 78 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. 79 | */ 80 | - (void)sd_setImageWithURL:(nullable NSURL *)url 81 | placeholderImage:(nullable UIImage *)placeholder 82 | options:(SDWebImageOptions)options; 83 | 84 | /** 85 | * Set the imageView `image` with an `url`. 86 | * 87 | * The download is asynchronous and cached. 88 | * 89 | * @param url The url for the image. 90 | * @param completedBlock A block called when operation has been completed. This block has no return value 91 | * and takes the requested UIImage as first parameter. In case of error the image parameter 92 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 93 | * indicating if the image was retrieved from the local cache or from the network. 94 | * The fourth parameter is the original image url. 95 | */ 96 | - (void)sd_setImageWithURL:(nullable NSURL *)url 97 | completed:(nullable SDExternalCompletionBlock)completedBlock; 98 | 99 | /** 100 | * Set the imageView `image` with an `url`, placeholder. 101 | * 102 | * The download is asynchronous and cached. 103 | * 104 | * @param url The url for the image. 105 | * @param placeholder The image to be set initially, until the image request finishes. 106 | * @param completedBlock A block called when operation has been completed. This block has no return value 107 | * and takes the requested UIImage as first parameter. In case of error the image parameter 108 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 109 | * indicating if the image was retrieved from the local cache or from the network. 110 | * The fourth parameter is the original image url. 111 | */ 112 | - (void)sd_setImageWithURL:(nullable NSURL *)url 113 | placeholderImage:(nullable UIImage *)placeholder 114 | completed:(nullable SDExternalCompletionBlock)completedBlock; 115 | 116 | /** 117 | * Set the imageView `image` with an `url`, placeholder and custom options. 118 | * 119 | * The download is asynchronous and cached. 120 | * 121 | * @param url The url for the image. 122 | * @param placeholder The image to be set initially, until the image request finishes. 123 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. 124 | * @param completedBlock A block called when operation has been completed. This block has no return value 125 | * and takes the requested UIImage as first parameter. In case of error the image parameter 126 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 127 | * indicating if the image was retrieved from the local cache or from the network. 128 | * The fourth parameter is the original image url. 129 | */ 130 | - (void)sd_setImageWithURL:(nullable NSURL *)url 131 | placeholderImage:(nullable UIImage *)placeholder 132 | options:(SDWebImageOptions)options 133 | completed:(nullable SDExternalCompletionBlock)completedBlock; 134 | 135 | /** 136 | * Set the imageView `image` with an `url`, placeholder and custom options. 137 | * 138 | * The download is asynchronous and cached. 139 | * 140 | * @param url The url for the image. 141 | * @param placeholder The image to be set initially, until the image request finishes. 142 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. 143 | * @param progressBlock A block called while image is downloading 144 | * @note the progress block is executed on a background queue 145 | * @param completedBlock A block called when operation has been completed. This block has no return value 146 | * and takes the requested UIImage as first parameter. In case of error the image parameter 147 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 148 | * indicating if the image was retrieved from the local cache or from the network. 149 | * The fourth parameter is the original image url. 150 | */ 151 | - (void)sd_setImageWithURL:(nullable NSURL *)url 152 | placeholderImage:(nullable UIImage *)placeholder 153 | options:(SDWebImageOptions)options 154 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 155 | completed:(nullable SDExternalCompletionBlock)completedBlock; 156 | 157 | /** 158 | * Set the imageView `image` with an `url` and optionally a placeholder image. 159 | * 160 | * The download is asynchronous and cached. 161 | * 162 | * @param url The url for the image. 163 | * @param placeholder The image to be set initially, until the image request finishes. 164 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. 165 | * @param progressBlock A block called while image is downloading 166 | * @note the progress block is executed on a background queue 167 | * @param completedBlock A block called when operation has been completed. This block has no return value 168 | * and takes the requested UIImage as first parameter. In case of error the image parameter 169 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 170 | * indicating if the image was retrieved from the local cache or from the network. 171 | * The fourth parameter is the original image url. 172 | */ 173 | - (void)sd_setImageWithPreviousCachedImageWithURL:(nullable NSURL *)url 174 | placeholderImage:(nullable UIImage *)placeholder 175 | options:(SDWebImageOptions)options 176 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 177 | completed:(nullable SDExternalCompletionBlock)completedBlock; 178 | 179 | #if SD_UIKIT 180 | 181 | #pragma mark - Animation of multiple images 182 | 183 | /** 184 | * Download an array of images and starts them in an animation loop 185 | * 186 | * @param arrayOfURLs An array of NSURL 187 | */ 188 | - (void)sd_setAnimationImagesWithURLs:(nonnull NSArray *)arrayOfURLs; 189 | 190 | - (void)sd_cancelCurrentAnimationImagesLoad; 191 | 192 | #endif 193 | 194 | @end 195 | 196 | #endif 197 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Downloader/SDWebImageDownloader.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import 10 | #import "SDWebImageCompat.h" 11 | #import "SDWebImageOperation.h" 12 | 13 | //下载选项 14 | typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) { 15 | SDWebImageDownloaderLowPriority = 1 << 0, 16 | SDWebImageDownloaderProgressiveDownload = 1 << 1, 17 | 18 | /** 19 | * By default, request prevent the use of NSURLCache. With this flag, NSURLCache 20 | * is used with default policies. 21 | */ 22 | SDWebImageDownloaderUseNSURLCache = 1 << 2, 23 | 24 | /** 25 | * Call completion block with nil image/imageData if the image was read from NSURLCache 26 | * (to be combined with `SDWebImageDownloaderUseNSURLCache`). 27 | */ 28 | 29 | SDWebImageDownloaderIgnoreCachedResponse = 1 << 3, 30 | /** 31 | * In iOS 4+, continue the download of the image if the app goes to background. This is achieved by asking the system for 32 | * extra time in background to let the request finish. If the background task expires the operation will be cancelled. 33 | */ 34 | 35 | SDWebImageDownloaderContinueInBackground = 1 << 4, 36 | 37 | /** 38 | * Handles cookies stored in NSHTTPCookieStore by setting 39 | * NSMutableURLRequest.HTTPShouldHandleCookies = YES; 40 | */ 41 | SDWebImageDownloaderHandleCookies = 1 << 5, 42 | 43 | /** 44 | * Enable to allow untrusted SSL certificates. 45 | * Useful for testing purposes. Use with caution in production. 46 | */ 47 | SDWebImageDownloaderAllowInvalidSSLCertificates = 1 << 6, 48 | 49 | /** 50 | * Put the image in the high priority queue. 51 | */ 52 | SDWebImageDownloaderHighPriority = 1 << 7, 53 | 54 | /** 55 | * Scale down the image 56 | */ 57 | SDWebImageDownloaderScaleDownLargeImages = 1 << 8, 58 | }; 59 | 60 | typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) { 61 | /** 62 | * Default value. All download operations will execute in queue style (first-in-first-out). 63 | */ 64 | SDWebImageDownloaderFIFOExecutionOrder, 65 | 66 | /** 67 | * All download operations will execute in stack style (last-in-first-out). 68 | */ 69 | SDWebImageDownloaderLIFOExecutionOrder 70 | }; 71 | 72 | extern NSString * _Nonnull const SDWebImageDownloadStartNotification; 73 | extern NSString * _Nonnull const SDWebImageDownloadStopNotification; 74 | 75 | typedef void(^SDWebImageDownloaderProgressBlock)(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL); 76 | 77 | typedef void(^SDWebImageDownloaderCompletedBlock)(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished); 78 | 79 | typedef NSDictionary SDHTTPHeadersDictionary; 80 | typedef NSMutableDictionary SDHTTPHeadersMutableDictionary; 81 | 82 | typedef SDHTTPHeadersDictionary * _Nullable (^SDWebImageDownloaderHeadersFilterBlock)(NSURL * _Nullable url, SDHTTPHeadersDictionary * _Nullable headers); 83 | 84 | /** 85 | * A token associated with each download. Can be used to cancel a download 86 | */ 87 | @interface SDWebImageDownloadToken : NSObject 88 | 89 | @property (nonatomic, strong, nullable) NSURL *url; 90 | @property (nonatomic, strong, nullable) id downloadOperationCancelToken; 91 | 92 | @end 93 | 94 | 95 | /** 96 | * Asynchronous downloader dedicated and optimized for image loading. 97 | */ 98 | @interface SDWebImageDownloader : NSObject 99 | 100 | /** 101 | * Decompressing images that are downloaded and cached can improve performance but can consume lot of memory. 102 | * Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption. 103 | */ 104 | @property (assign, nonatomic) BOOL shouldDecompressImages; 105 | 106 | /** 107 | * The maximum number of concurrent downloads 108 | */ 109 | @property (assign, nonatomic) NSInteger maxConcurrentDownloads; 110 | 111 | /** 112 | * Shows the current amount of downloads that still need to be downloaded 113 | */ 114 | @property (readonly, nonatomic) NSUInteger currentDownloadCount; 115 | 116 | 117 | /** 118 | * The timeout value (in seconds) for the download operation. Default: 15.0. 119 | */ 120 | @property (assign, nonatomic) NSTimeInterval downloadTimeout; 121 | 122 | 123 | /** 124 | * Changes download operations execution order. Default value is `SDWebImageDownloaderFIFOExecutionOrder`. 125 | */ 126 | @property (assign, nonatomic) SDWebImageDownloaderExecutionOrder executionOrder; 127 | 128 | /** 129 | * Singleton method, returns the shared instance 130 | * 131 | * @return global shared instance of downloader class 132 | */ 133 | + (nonnull instancetype)sharedDownloader; 134 | 135 | /** 136 | * Set the default URL credential to be set for request operations. 137 | */ 138 | @property (strong, nonatomic, nullable) NSURLCredential *urlCredential; 139 | 140 | /** 141 | * Set username 142 | */ 143 | @property (strong, nonatomic, nullable) NSString *username; 144 | 145 | /** 146 | * Set password 147 | */ 148 | @property (strong, nonatomic, nullable) NSString *password; 149 | 150 | /** 151 | * Set filter to pick headers for downloading image HTTP request. 152 | * 153 | * This block will be invoked for each downloading image request, returned 154 | * NSDictionary will be used as headers in corresponding HTTP request. 155 | */ 156 | @property (nonatomic, copy, nullable) SDWebImageDownloaderHeadersFilterBlock headersFilter; 157 | 158 | /** 159 | * Creates an instance of a downloader with specified session configuration. 160 | * *Note*: `timeoutIntervalForRequest` is going to be overwritten. 161 | * @return new instance of downloader class 162 | */ 163 | - (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration NS_DESIGNATED_INITIALIZER; 164 | 165 | /** 166 | * Set a value for a HTTP header to be appended to each download HTTP request. 167 | * 168 | * @param value The value for the header field. Use `nil` value to remove the header. 169 | * @param field The name of the header field to set. 170 | */ 171 | - (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)field; 172 | 173 | /** 174 | * Returns the value of the specified HTTP header field. 175 | * 176 | * @return The value associated with the header field field, or `nil` if there is no corresponding header field. 177 | */ 178 | - (nullable NSString *)valueForHTTPHeaderField:(nullable NSString *)field; 179 | 180 | /** 181 | * Sets a subclass of `SDWebImageDownloaderOperation` as the default 182 | * `NSOperation` to be used each time SDWebImage constructs a request 183 | * operation to download an image. 184 | * 185 | * @param operationClass The subclass of `SDWebImageDownloaderOperation` to set 186 | * as default. Passing `nil` will revert to `SDWebImageDownloaderOperation`. 187 | */ 188 | - (void)setOperationClass:(nullable Class)operationClass; 189 | 190 | /** 191 | * Creates a SDWebImageDownloader async downloader instance with a given URL 192 | * 193 | * The delegate will be informed when the image is finish downloaded or an error has happen. 194 | * 195 | * @see SDWebImageDownloaderDelegate 196 | * 197 | * @param url The URL to the image to download 198 | * @param options The options to be used for this download 199 | * @param progressBlock A block called repeatedly while the image is downloading 200 | * @note the progress block is executed on a background queue 201 | * @param completedBlock A block called once the download is completed. 202 | * If the download succeeded, the image parameter is set, in case of error, 203 | * error parameter is set with the error. The last parameter is always YES 204 | * if SDWebImageDownloaderProgressiveDownload isn't use. With the 205 | * SDWebImageDownloaderProgressiveDownload option, this block is called 206 | * repeatedly with the partial image object and the finished argument set to NO 207 | * before to be called a last time with the full image and finished argument 208 | * set to YES. In case of error, the finished argument is always YES. 209 | * 210 | * @return A token (SDWebImageDownloadToken) that can be passed to -cancel: to cancel this operation 211 | */ 212 | - (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url 213 | options:(SDWebImageDownloaderOptions)options 214 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 215 | completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock; 216 | 217 | /** 218 | * Cancels a download that was previously queued using -downloadImageWithURL:options:progress:completed: 219 | * 220 | * @param token The token received from -downloadImageWithURL:options:progress:completed: that should be canceled. 221 | */ 222 | - (void)cancel:(nullable SDWebImageDownloadToken *)token; 223 | 224 | /** 225 | * Sets the download queue suspension state 226 | */ 227 | - (void)setSuspended:(BOOL)suspended; 228 | 229 | /** 230 | * Cancels all download operations in the queue 231 | */ 232 | - (void)cancelAllDownloads; 233 | 234 | @end 235 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Cache/SDImageCache.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import 10 | #import "SDWebImageCompat.h" 11 | 12 | @class SDImageCacheConfig; 13 | 14 | typedef NS_ENUM(NSInteger, SDImageCacheType) { 15 | /** 16 | * The image wasn't available the SDWebImage caches, but was downloaded from the web. 17 | */ 18 | SDImageCacheTypeNone, 19 | /** 20 | * The image was obtained from the disk cache. 21 | */ 22 | SDImageCacheTypeDisk, 23 | /** 24 | * The image was obtained from the memory cache. 25 | */ 26 | SDImageCacheTypeMemory 27 | }; 28 | 29 | typedef void(^SDCacheQueryCompletedBlock)(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType); 30 | 31 | typedef void(^SDWebImageCheckCacheCompletionBlock)(BOOL isInCache); 32 | 33 | typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger totalSize); 34 | 35 | 36 | /** 37 | * SDImageCache maintains a memory cache and an optional disk cache. Disk cache write operations are performed 38 | * asynchronous so it doesn’t add unnecessary latency to the UI. 39 | */ 40 | @interface SDImageCache : NSObject 41 | 42 | #pragma mark - Properties 43 | 44 | /** 45 | * Cache Config object - storing all kind of settings 46 | */ 47 | @property (nonatomic, nonnull, readonly) SDImageCacheConfig *config; 48 | 49 | /** 50 | * The maximum "total cost" of the in-memory image cache. The cost function is the number of pixels held in memory. 51 | */ 52 | @property (assign, nonatomic) NSUInteger maxMemoryCost; 53 | 54 | /** 55 | * The maximum number of objects the cache should hold. 56 | */ 57 | @property (assign, nonatomic) NSUInteger maxMemoryCountLimit; 58 | 59 | #pragma mark - Singleton and initialization 60 | 61 | /** 62 | * Returns global shared cache instance 63 | * 64 | * @return SDImageCache global instance 65 | */ 66 | + (nonnull instancetype)sharedImageCache; 67 | 68 | /** 69 | * Init a new cache store with a specific namespace 70 | * 71 | * @param ns The namespace to use for this cache store 72 | */ 73 | - (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns; 74 | 75 | /** 76 | * Init a new cache store with a specific namespace and directory 77 | * 78 | * @param ns The namespace to use for this cache store 79 | * @param directory Directory to cache disk images in 80 | */ 81 | - (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns 82 | diskCacheDirectory:(nonnull NSString *)directory NS_DESIGNATED_INITIALIZER; 83 | 84 | #pragma mark - Cache paths 85 | 86 | - (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace; 87 | 88 | /** 89 | * Add a read-only cache path to search for images pre-cached by SDImageCache 90 | * Useful if you want to bundle pre-loaded images with your app 91 | * 92 | * @param path The path to use for this read-only cache path 93 | */ 94 | - (void)addReadOnlyCachePath:(nonnull NSString *)path; 95 | 96 | #pragma mark - Store Ops 97 | 98 | /** 99 | * Asynchronously store an image into memory and disk cache at the given key. 100 | * 101 | * @param image The image to store 102 | * @param key The unique image cache key, usually it's image absolute URL 103 | * @param completionBlock A block executed after the operation is finished 104 | */ 105 | - (void)storeImage:(nullable UIImage *)image 106 | forKey:(nullable NSString *)key 107 | completion:(nullable SDWebImageNoParamsBlock)completionBlock; 108 | 109 | /** 110 | * Asynchronously store an image into memory and disk cache at the given key. 111 | * 112 | * @param image The image to store 113 | * @param key The unique image cache key, usually it's image absolute URL 114 | * @param toDisk Store the image to disk cache if YES 115 | * @param completionBlock A block executed after the operation is finished 116 | */ 117 | - (void)storeImage:(nullable UIImage *)image 118 | forKey:(nullable NSString *)key 119 | toDisk:(BOOL)toDisk 120 | completion:(nullable SDWebImageNoParamsBlock)completionBlock; 121 | 122 | /** 123 | * Asynchronously store an image into memory and disk cache at the given key. 124 | * 125 | * @param image The image to store 126 | * @param imageData The image data as returned by the server, this representation will be used for disk storage 127 | * instead of converting the given image object into a storable/compressed image format in order 128 | * to save quality and CPU 129 | * @param key The unique image cache key, usually it's image absolute URL 130 | * @param toDisk Store the image to disk cache if YES 131 | * @param completionBlock A block executed after the operation is finished 132 | */ 133 | - (void)storeImage:(nullable UIImage *)image 134 | imageData:(nullable NSData *)imageData 135 | forKey:(nullable NSString *)key 136 | toDisk:(BOOL)toDisk 137 | completion:(nullable SDWebImageNoParamsBlock)completionBlock; 138 | 139 | /** 140 | * Synchronously store image NSData into disk cache at the given key. 141 | * 142 | * @warning This method is synchronous, make sure to call it from the ioQueue 143 | * 144 | * @param imageData The image data to store 145 | * @param key The unique image cache key, usually it's image absolute URL 146 | */ 147 | - (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key; 148 | 149 | #pragma mark - Query and Retrieve Ops 150 | 151 | /** 152 | * Async check if image exists in disk cache already (does not load the image) 153 | * 154 | * @param key the key describing the url 155 | * @param completionBlock the block to be executed when the check is done. 156 | * @note the completion block will be always executed on the main queue 157 | */ 158 | - (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock; 159 | 160 | /** 161 | * Operation that queries the cache asynchronously and call the completion when done. 162 | * 163 | * @param key The unique key used to store the wanted image 164 | * @param doneBlock The completion block. Will not get called if the operation is cancelled 165 | * 166 | * @return a NSOperation instance containing the cache op 167 | */ 168 | - (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock; 169 | 170 | /** 171 | * Query the memory cache synchronously. 172 | * 173 | * @param key The unique key used to store the image 174 | */ 175 | - (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key; 176 | 177 | /** 178 | * Query the disk cache synchronously. 179 | * 180 | * @param key The unique key used to store the image 181 | */ 182 | - (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key; 183 | 184 | /** 185 | * Query the cache (memory and or disk) synchronously after checking the memory cache. 186 | * 187 | * @param key The unique key used to store the image 188 | */ 189 | - (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key; 190 | 191 | #pragma mark - Remove Ops 192 | 193 | /** 194 | * Remove the image from memory and disk cache asynchronously 195 | * 196 | * @param key The unique image cache key 197 | * @param completion A block that should be executed after the image has been removed (optional) 198 | */ 199 | - (void)removeImageForKey:(nullable NSString *)key withCompletion:(nullable SDWebImageNoParamsBlock)completion; 200 | 201 | /** 202 | * Remove the image from memory and optionally disk cache asynchronously 203 | * 204 | * @param key The unique image cache key 205 | * @param fromDisk Also remove cache entry from disk if YES 206 | * @param completion A block that should be executed after the image has been removed (optional) 207 | */ 208 | - (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion; 209 | 210 | #pragma mark - Cache clean Ops 211 | 212 | /** 213 | * Clear all memory cached images 214 | */ 215 | - (void)clearMemory; 216 | 217 | /** 218 | * Async clear all disk cached images. Non-blocking method - returns immediately. 219 | * @param completion A block that should be executed after cache expiration completes (optional) 220 | */ 221 | - (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion; 222 | 223 | /** 224 | * Async remove all expired cached image from disk. Non-blocking method - returns immediately. 225 | * @param completionBlock A block that should be executed after cache expiration completes (optional) 226 | */ 227 | - (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock; 228 | 229 | #pragma mark - Cache Info 230 | 231 | /** 232 | * Get the size used by the disk cache 233 | */ 234 | - (NSUInteger)getSize; 235 | 236 | /** 237 | * Get the number of images in the disk cache 238 | */ 239 | - (NSUInteger)getDiskCount; 240 | 241 | /** 242 | * Asynchronously calculate the disk cache's size. 243 | */ 244 | - (void)calculateSizeWithCompletionBlock:(nullable SDWebImageCalculateSizeBlock)completionBlock; 245 | 246 | #pragma mark - Cache Paths 247 | 248 | /** 249 | * Get the cache path for a certain key (needs the cache path root folder) 250 | * 251 | * @param key the key (can be obtained from url using cacheKeyForURL) 252 | * @param path the cache path root folder 253 | * 254 | * @return the cache path 255 | */ 256 | - (nullable NSString *)cachePathForKey:(nullable NSString *)key inPath:(nonnull NSString *)path; 257 | 258 | /** 259 | * Get the default cache path for a certain key 260 | * 261 | * @param key the key (can be obtained from url using cacheKeyForURL) 262 | * 263 | * @return the default cache path 264 | */ 265 | - (nullable NSString *)defaultCachePathForKey:(nullable NSString *)key; 266 | 267 | @end 268 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Utils/SDWebImageManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDWebImageCompat.h" 10 | #import "SDWebImageOperation.h" 11 | #import "SDWebImageDownloader.h" 12 | #import "SDImageCache.h" 13 | 14 | typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) { 15 | /** 16 | * By default, when a URL fail to be downloaded, the URL is blacklisted so the library won't keep trying. 17 | * This flag disable this blacklisting. 18 | */ 19 | SDWebImageRetryFailed = 1 << 0, 20 | 21 | /** 22 | * By default, image downloads are started during UI interactions, this flags disable this feature, 23 | * leading to delayed download on UIScrollView deceleration for instance. 24 | */ 25 | SDWebImageLowPriority = 1 << 1, 26 | 27 | /** 28 | * This flag disables on-disk caching 29 | */ 30 | SDWebImageCacheMemoryOnly = 1 << 2, 31 | 32 | /** 33 | * This flag enables progressive download, the image is displayed progressively during download as a browser would do. 34 | * By default, the image is only displayed once completely downloaded. 35 | */ 36 | SDWebImageProgressiveDownload = 1 << 3, 37 | 38 | /** 39 | * Even if the image is cached, respect the HTTP response cache control, and refresh the image from remote location if needed. 40 | * The disk caching will be handled by NSURLCache instead of SDWebImage leading to slight performance degradation. 41 | * This option helps deal with images changing behind the same request URL, e.g. Facebook graph api profile pics. 42 | * If a cached image is refreshed, the completion block is called once with the cached image and again with the final image. 43 | * 44 | * Use this flag only if you can't make your URLs static with embedded cache busting parameter. 45 | */ 46 | SDWebImageRefreshCached = 1 << 4, 47 | 48 | /** 49 | * In iOS 4+, continue the download of the image if the app goes to background. This is achieved by asking the system for 50 | * extra time in background to let the request finish. If the background task expires the operation will be cancelled. 51 | */ 52 | SDWebImageContinueInBackground = 1 << 5, 53 | 54 | /** 55 | * Handles cookies stored in NSHTTPCookieStore by setting 56 | * NSMutableURLRequest.HTTPShouldHandleCookies = YES; 57 | */ 58 | SDWebImageHandleCookies = 1 << 6, 59 | 60 | /** 61 | * Enable to allow untrusted SSL certificates. 62 | * Useful for testing purposes. Use with caution in production. 63 | */ 64 | SDWebImageAllowInvalidSSLCertificates = 1 << 7, 65 | 66 | /** 67 | * By default, images are loaded in the order in which they were queued. This flag moves them to 68 | * the front of the queue. 69 | */ 70 | SDWebImageHighPriority = 1 << 8, 71 | 72 | /** 73 | * By default, placeholder images are loaded while the image is loading. This flag will delay the loading 74 | * of the placeholder image until after the image has finished loading. 75 | */ 76 | SDWebImageDelayPlaceholder = 1 << 9, 77 | 78 | /** 79 | * We usually don't call transformDownloadedImage delegate method on animated images, 80 | * as most transformation code would mangle it. 81 | * Use this flag to transform them anyway. 82 | */ 83 | SDWebImageTransformAnimatedImage = 1 << 10, 84 | 85 | /** 86 | * By default, image is added to the imageView after download. But in some cases, we want to 87 | * have the hand before setting the image (apply a filter or add it with cross-fade animation for instance) 88 | * Use this flag if you want to manually set the image in the completion when success 89 | */ 90 | SDWebImageAvoidAutoSetImage = 1 << 11, 91 | 92 | /** 93 | * By default, images are decoded respecting their original size. On iOS, this flag will scale down the 94 | * images to a size compatible with the constrained memory of devices. 95 | * If `SDWebImageProgressiveDownload` flag is set the scale down is deactivated. 96 | */ 97 | SDWebImageScaleDownLargeImages = 1 << 12 98 | }; 99 | 100 | typedef void(^SDExternalCompletionBlock)(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL); 101 | 102 | typedef void(^SDInternalCompletionBlock)(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL); 103 | 104 | typedef NSString * _Nullable (^SDWebImageCacheKeyFilterBlock)(NSURL * _Nullable url); 105 | 106 | 107 | @class SDWebImageManager; 108 | 109 | @protocol SDWebImageManagerDelegate 110 | 111 | @optional 112 | 113 | /** 114 | * Controls which image should be downloaded when the image is not found in the cache. 115 | * 116 | * @param imageManager The current `SDWebImageManager` 117 | * @param imageURL The url of the image to be downloaded 118 | * 119 | * @return Return NO to prevent the downloading of the image on cache misses. If not implemented, YES is implied. 120 | */ 121 | - (BOOL)imageManager:(nonnull SDWebImageManager *)imageManager shouldDownloadImageForURL:(nullable NSURL *)imageURL; 122 | 123 | /** 124 | * Allows to transform the image immediately after it has been downloaded and just before to cache it on disk and memory. 125 | * NOTE: This method is called from a global queue in order to not to block the main thread. 126 | * 127 | * @param imageManager The current `SDWebImageManager` 128 | * @param image The image to transform 129 | * @param imageURL The url of the image to transform 130 | * 131 | * @return The transformed image object. 132 | */ 133 | - (nullable UIImage *)imageManager:(nonnull SDWebImageManager *)imageManager transformDownloadedImage:(nullable UIImage *)image withURL:(nullable NSURL *)imageURL; 134 | 135 | @end 136 | 137 | /** 138 | * The SDWebImageManager is the class behind the UIImageView+WebCache category and likes. 139 | * It ties the asynchronous downloader (SDWebImageDownloader) with the image cache store (SDImageCache). 140 | * You can use this class directly to benefit from web image downloading with caching in another context than 141 | * a UIView. 142 | * 143 | * Here is a simple example of how to use SDWebImageManager: 144 | * 145 | * @code 146 | 147 | SDWebImageManager *manager = [SDWebImageManager sharedManager]; 148 | [manager loadImageWithURL:imageURL 149 | options:0 150 | progress:nil 151 | completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { 152 | if (image) { 153 | // do something with image 154 | } 155 | }]; 156 | 157 | * @endcode 158 | */ 159 | @interface SDWebImageManager : NSObject 160 | 161 | @property (weak, nonatomic, nullable) id delegate; 162 | 163 | @property (strong, nonatomic, readonly, nullable) SDImageCache *imageCache; 164 | @property (strong, nonatomic, readonly, nullable) SDWebImageDownloader *imageDownloader; 165 | 166 | /** 167 | * The cache filter is a block used each time SDWebImageManager need to convert an URL into a cache key. This can 168 | * be used to remove dynamic part of an image URL. 169 | * 170 | * The following example sets a filter in the application delegate that will remove any query-string from the 171 | * URL before to use it as a cache key: 172 | * 173 | * @code 174 | 175 | [[SDWebImageManager sharedManager] setCacheKeyFilter:^(NSURL *url) { 176 | url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path]; 177 | return [url absoluteString]; 178 | }]; 179 | 180 | * @endcode 181 | */ 182 | @property (nonatomic, copy, nullable) SDWebImageCacheKeyFilterBlock cacheKeyFilter; 183 | 184 | /** 185 | * Returns global SDWebImageManager instance. 186 | * 187 | * @return SDWebImageManager shared instance 188 | */ 189 | + (nonnull instancetype)sharedManager; 190 | 191 | /** 192 | * Allows to specify instance of cache and image downloader used with image manager. 193 | * @return new instance of `SDWebImageManager` with specified cache and downloader. 194 | */ 195 | - (nonnull instancetype)initWithCache:(nonnull SDImageCache *)cache downloader:(nonnull SDWebImageDownloader *)downloader NS_DESIGNATED_INITIALIZER; 196 | 197 | /** 198 | * Downloads the image at the given URL if not present in cache or return the cached version otherwise. 199 | * 200 | * @param url The URL to the image 201 | * @param options A mask to specify options to use for this request 202 | * @param progressBlock A block called while image is downloading 203 | * @note the progress block is executed on a background queue 204 | * @param completedBlock A block called when operation has been completed. 205 | * 206 | * This parameter is required. 207 | * 208 | * This block has no return value and takes the requested UIImage as first parameter and the NSData representation as second parameter. 209 | * In case of error the image parameter is nil and the third parameter may contain an NSError. 210 | * 211 | * The forth parameter is an `SDImageCacheType` enum indicating if the image was retrieved from the local cache 212 | * or from the memory cache or from the network. 213 | * 214 | * The fith parameter is set to NO when the SDWebImageProgressiveDownload option is used and the image is 215 | * downloading. This block is thus called repeatedly with a partial image. When image is fully downloaded, the 216 | * block is called a last time with the full image and the last parameter set to YES. 217 | * 218 | * The last parameter is the original image URL 219 | * 220 | * @return Returns an NSObject conforming to SDWebImageOperation. Should be an instance of SDWebImageDownloaderOperation 221 | */ 222 | - (nullable id )loadImageWithURL:(nullable NSURL *)url 223 | options:(SDWebImageOptions)options 224 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 225 | completed:(nullable SDInternalCompletionBlock)completedBlock; 226 | 227 | /** 228 | * Saves image to cache for given URL 229 | * 230 | * @param image The image to cache 231 | * @param url The URL to the image 232 | * 233 | */ 234 | 235 | - (void)saveImageToCache:(nullable UIImage *)image forURL:(nullable NSURL *)url; 236 | 237 | /** 238 | * Cancel all current operations 239 | */ 240 | - (void)cancelAll; 241 | 242 | /** 243 | * Check one or more operations running 244 | */ 245 | - (BOOL)isRunning; 246 | 247 | /** 248 | * Async check if image has already been cached 249 | * 250 | * @param url image url 251 | * @param completionBlock the block to be executed when the check is finished 252 | * 253 | * @note the completion block is always executed on the main queue 254 | */ 255 | - (void)cachedImageExistsForURL:(nullable NSURL *)url 256 | completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock; 257 | 258 | /** 259 | * Async check if image has already been cached on disk only 260 | * 261 | * @param url image url 262 | * @param completionBlock the block to be executed when the check is finished 263 | * 264 | * @note the completion block is always executed on the main queue 265 | */ 266 | - (void)diskImageExistsForURL:(nullable NSURL *)url 267 | completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock; 268 | 269 | 270 | /** 271 | *Return the cache key for a given URL 272 | */ 273 | - (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url; 274 | 275 | @end 276 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/UI_Categories/UIButton+WebCache.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDWebImageCompat.h" 10 | 11 | #if SD_UIKIT 12 | 13 | #import "SDWebImageManager.h" 14 | 15 | /** 16 | * Integrates SDWebImage async downloading and caching of remote images with UIButtonView. 17 | */ 18 | @interface UIButton (WebCache) 19 | 20 | /** 21 | * Get the current image URL. 22 | */ 23 | - (nullable NSURL *)sd_currentImageURL; 24 | 25 | #pragma mark - Image 26 | 27 | /** 28 | * Get the image URL for a control state. 29 | * 30 | * @param state Which state you want to know the URL for. The values are described in UIControlState. 31 | */ 32 | - (nullable NSURL *)sd_imageURLForState:(UIControlState)state; 33 | 34 | /** 35 | * Set the imageView `image` with an `url`. 36 | * 37 | * The download is asynchronous and cached. 38 | * 39 | * @param url The url for the image. 40 | * @param state The state that uses the specified title. The values are described in UIControlState. 41 | */ 42 | - (void)sd_setImageWithURL:(nullable NSURL *)url 43 | forState:(UIControlState)state; 44 | 45 | /** 46 | * Set the imageView `image` with an `url` and a placeholder. 47 | * 48 | * The download is asynchronous and cached. 49 | * 50 | * @param url The url for the image. 51 | * @param state The state that uses the specified title. The values are described in UIControlState. 52 | * @param placeholder The image to be set initially, until the image request finishes. 53 | * @see sd_setImageWithURL:placeholderImage:options: 54 | */ 55 | - (void)sd_setImageWithURL:(nullable NSURL *)url 56 | forState:(UIControlState)state 57 | placeholderImage:(nullable UIImage *)placeholder; 58 | 59 | /** 60 | * Set the imageView `image` with an `url`, placeholder and custom options. 61 | * 62 | * The download is asynchronous and cached. 63 | * 64 | * @param url The url for the image. 65 | * @param state The state that uses the specified title. The values are described in UIControlState. 66 | * @param placeholder The image to be set initially, until the image request finishes. 67 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. 68 | */ 69 | - (void)sd_setImageWithURL:(nullable NSURL *)url 70 | forState:(UIControlState)state 71 | placeholderImage:(nullable UIImage *)placeholder 72 | options:(SDWebImageOptions)options; 73 | 74 | /** 75 | * Set the imageView `image` with an `url`. 76 | * 77 | * The download is asynchronous and cached. 78 | * 79 | * @param url The url for the image. 80 | * @param state The state that uses the specified title. The values are described in UIControlState. 81 | * @param completedBlock A block called when operation has been completed. This block has no return value 82 | * and takes the requested UIImage as first parameter. In case of error the image parameter 83 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 84 | * indicating if the image was retrieved from the local cache or from the network. 85 | * The fourth parameter is the original image url. 86 | */ 87 | - (void)sd_setImageWithURL:(nullable NSURL *)url 88 | forState:(UIControlState)state 89 | completed:(nullable SDExternalCompletionBlock)completedBlock; 90 | 91 | /** 92 | * Set the imageView `image` with an `url`, placeholder. 93 | * 94 | * The download is asynchronous and cached. 95 | * 96 | * @param url The url for the image. 97 | * @param state The state that uses the specified title. The values are described in UIControlState. 98 | * @param placeholder The image to be set initially, until the image request finishes. 99 | * @param completedBlock A block called when operation has been completed. This block has no return value 100 | * and takes the requested UIImage as first parameter. In case of error the image parameter 101 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 102 | * indicating if the image was retrieved from the local cache or from the network. 103 | * The fourth parameter is the original image url. 104 | */ 105 | - (void)sd_setImageWithURL:(nullable NSURL *)url 106 | forState:(UIControlState)state 107 | placeholderImage:(nullable UIImage *)placeholder 108 | completed:(nullable SDExternalCompletionBlock)completedBlock; 109 | 110 | /** 111 | * Set the imageView `image` with an `url`, placeholder and custom options. 112 | * 113 | * The download is asynchronous and cached. 114 | * 115 | * @param url The url for the image. 116 | * @param state The state that uses the specified title. The values are described in UIControlState. 117 | * @param placeholder The image to be set initially, until the image request finishes. 118 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. 119 | * @param completedBlock A block called when operation has been completed. This block has no return value 120 | * and takes the requested UIImage as first parameter. In case of error the image parameter 121 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 122 | * indicating if the image was retrieved from the local cache or from the network. 123 | * The fourth parameter is the original image url. 124 | */ 125 | - (void)sd_setImageWithURL:(nullable NSURL *)url 126 | forState:(UIControlState)state 127 | placeholderImage:(nullable UIImage *)placeholder 128 | options:(SDWebImageOptions)options 129 | completed:(nullable SDExternalCompletionBlock)completedBlock; 130 | 131 | #pragma mark - Background image 132 | 133 | /** 134 | * Set the backgroundImageView `image` with an `url`. 135 | * 136 | * The download is asynchronous and cached. 137 | * 138 | * @param url The url for the image. 139 | * @param state The state that uses the specified title. The values are described in UIControlState. 140 | */ 141 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url 142 | forState:(UIControlState)state; 143 | 144 | /** 145 | * Set the backgroundImageView `image` with an `url` and a placeholder. 146 | * 147 | * The download is asynchronous and cached. 148 | * 149 | * @param url The url for the image. 150 | * @param state The state that uses the specified title. The values are described in UIControlState. 151 | * @param placeholder The image to be set initially, until the image request finishes. 152 | * @see sd_setImageWithURL:placeholderImage:options: 153 | */ 154 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url 155 | forState:(UIControlState)state 156 | placeholderImage:(nullable UIImage *)placeholder; 157 | 158 | /** 159 | * Set the backgroundImageView `image` with an `url`, placeholder and custom options. 160 | * 161 | * The download is asynchronous and cached. 162 | * 163 | * @param url The url for the image. 164 | * @param state The state that uses the specified title. The values are described in UIControlState. 165 | * @param placeholder The image to be set initially, until the image request finishes. 166 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. 167 | */ 168 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url 169 | forState:(UIControlState)state 170 | placeholderImage:(nullable UIImage *)placeholder 171 | options:(SDWebImageOptions)options; 172 | 173 | /** 174 | * Set the backgroundImageView `image` with an `url`. 175 | * 176 | * The download is asynchronous and cached. 177 | * 178 | * @param url The url for the image. 179 | * @param state The state that uses the specified title. The values are described in UIControlState. 180 | * @param completedBlock A block called when operation has been completed. This block has no return value 181 | * and takes the requested UIImage as first parameter. In case of error the image parameter 182 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 183 | * indicating if the image was retrieved from the local cache or from the network. 184 | * The fourth parameter is the original image url. 185 | */ 186 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url 187 | forState:(UIControlState)state 188 | completed:(nullable SDExternalCompletionBlock)completedBlock; 189 | 190 | /** 191 | * Set the backgroundImageView `image` with an `url`, placeholder. 192 | * 193 | * The download is asynchronous and cached. 194 | * 195 | * @param url The url for the image. 196 | * @param state The state that uses the specified title. The values are described in UIControlState. 197 | * @param placeholder The image to be set initially, until the image request finishes. 198 | * @param completedBlock A block called when operation has been completed. This block has no return value 199 | * and takes the requested UIImage as first parameter. In case of error the image parameter 200 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 201 | * indicating if the image was retrieved from the local cache or from the network. 202 | * The fourth parameter is the original image url. 203 | */ 204 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url 205 | forState:(UIControlState)state 206 | placeholderImage:(nullable UIImage *)placeholder 207 | completed:(nullable SDExternalCompletionBlock)completedBlock; 208 | 209 | /** 210 | * Set the backgroundImageView `image` with an `url`, placeholder and custom options. 211 | * 212 | * The download is asynchronous and cached. 213 | * 214 | * @param url The url for the image. 215 | * @param placeholder The image to be set initially, until the image request finishes. 216 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. 217 | * @param completedBlock A block called when operation has been completed. This block has no return value 218 | * and takes the requested UIImage as first parameter. In case of error the image parameter 219 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean 220 | * indicating if the image was retrieved from the local cache or from the network. 221 | * The fourth parameter is the original image url. 222 | */ 223 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url 224 | forState:(UIControlState)state 225 | placeholderImage:(nullable UIImage *)placeholder 226 | options:(SDWebImageOptions)options 227 | completed:(nullable SDExternalCompletionBlock)completedBlock; 228 | 229 | #pragma mark - Cancel 230 | 231 | /** 232 | * Cancel the current image download 233 | */ 234 | - (void)sd_cancelImageLoadForState:(UIControlState)state; 235 | 236 | /** 237 | * Cancel the current backgroundImage download 238 | */ 239 | - (void)sd_cancelBackgroundImageLoadForState:(UIControlState)state; 240 | 241 | @end 242 | 243 | #endif 244 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Utils/SDWebImageDecoder.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * (c) james 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | #import "SDWebImageDecoder.h" 11 | 12 | @implementation UIImage (ForceDecode) 13 | 14 | #if SD_UIKIT || SD_WATCH 15 | static const size_t kBytesPerPixel = 4; 16 | static const size_t kBitsPerComponent = 8; 17 | 18 | + (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image { 19 | //图片是否可以解压,若是不适合解压则直接返回原图片 20 | if (![UIImage shouldDecodeImage:image]) { 21 | return image; 22 | } 23 | 24 | 25 | // autorelease the bitmap context and all vars to help system to free memory when there are memory warning. 26 | // on iOS7, do not forget to call [[SDImageCache sharedImageCache] clearMemory]; 27 | @autoreleasepool{ 28 | 29 | CGImageRef imageRef = image.CGImage; 30 | //CGColorSpaceRef : A profile that specifies how to interpret a color value for display. 31 | CGColorSpaceRef colorspaceRef = [UIImage colorSpaceForImageRef:imageRef]; 32 | //宽度 33 | size_t width = CGImageGetWidth(imageRef); 34 | //高度 35 | size_t height = CGImageGetHeight(imageRef); 36 | //kBytesPerPixel 每个像素有4个字节 37 | //图片每行的数据量 38 | size_t bytesPerRow = kBytesPerPixel * width; 39 | 40 | // kCGImageAlphaNone is not supported in CGBitmapContextCreate. 41 | // Since the original image here has no alpha info, use kCGImageAlphaNoneSkipLast 42 | // to create bitmap graphics contexts without alpha info. 43 | // kBitsPerComponent(一个颜色由几个bit表示) : the number of bits allocated for a single color component of a bitmap image. 44 | CGContextRef context = CGBitmapContextCreate(NULL, 45 | width, 46 | height, 47 | kBitsPerComponent, 48 | bytesPerRow, 49 | colorspaceRef, 50 | kCGBitmapByteOrderDefault|kCGImageAlphaNoneSkipLast); 51 | if (context == NULL) { 52 | return image; 53 | } 54 | 55 | // Draw the image into the context and retrieve the new bitmap image without alpha 56 | CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); 57 | CGImageRef imageRefWithoutAlpha = CGBitmapContextCreateImage(context); 58 | // 在这个图片处理过程中丢失了图片的透明度信息 59 | UIImage *imageWithoutAlpha = [UIImage imageWithCGImage:imageRefWithoutAlpha 60 | scale:image.scale 61 | orientation:image.imageOrientation]; 62 | 63 | CGContextRelease(context); 64 | CGImageRelease(imageRefWithoutAlpha); 65 | // 没有透明度信息的图片 66 | return imageWithoutAlpha; 67 | } 68 | } 69 | 70 | /* 71 | * Defines the maximum size in MB of the decoded image when the flag `SDWebImageScaleDownLargeImages` is set 72 | * Suggested value for iPad1 and iPhone 3GS: 60. 73 | * Suggested value for iPad2 and iPhone 4: 120. 74 | * Suggested value for iPhone 3G and iPod 2 and earlier devices: 30. 75 | */ 76 | static const CGFloat kDestImageSizeMB = 60.0f;//解码图像的最大容量 77 | 78 | /* 79 | * Defines the maximum size in MB of a tile used to decode image when the flag `SDWebImageScaleDownLargeImages` is set 80 | * Suggested value for iPad1 and iPhone 3GS: 20. 81 | * Suggested value for iPad2 and iPhone 4: 40. 82 | * Suggested value for iPhone 3G and iPod 2 and earlier devices: 10. 83 | */ 84 | static const CGFloat kSourceImageTileSizeMB = 20.0f; 85 | 86 | static const CGFloat kBytesPerMB = 1024.0f * 1024.0f;//每MB有多少bytes 87 | static const CGFloat kPixelsPerMB = kBytesPerMB / kBytesPerPixel;//每MB有多少像素 88 | static const CGFloat kDestTotalPixels = kDestImageSizeMB * kPixelsPerMB;//图片的最大像素容量 89 | // The "tile" is the maximum amount of pixel data to load from the input image into memory at one time. 90 | static const CGFloat kTileTotalPixels = kSourceImageTileSizeMB * kPixelsPerMB; 91 | 92 | static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to overlap the seems where tiles meet. 93 | 94 | + (nullable UIImage *)decodedAndScaledDownImageWithImage:(nullable UIImage *)image { 95 | //是否需要解码图片 96 | if (![UIImage shouldDecodeImage:image]) { 97 | return image; 98 | } 99 | //是否需要缩小图片 100 | if (![UIImage shouldScaleDownImage:image]) { 101 | //不需要缩小的图片则进行图片解码 102 | return [UIImage decodedImageWithImage:image]; 103 | } 104 | 105 | CGContextRef destContext; 106 | // 图片缩小的代码来自Apple的官方Demo,里面有比较详细的注释 https://developer.apple.com/library/content/samplecode/LargeImageDownsizing/Introduction/Intro.html 107 | // autorelease the bitmap context and all vars to help system to free memory when there are memory warning. 108 | // on iOS7, do not forget to call [[SDImageCache sharedImageCache] clearMemory]; 109 | @autoreleasepool { 110 | CGImageRef sourceImageRef = image.CGImage; 111 | 112 | CGSize sourceResolution = CGSizeZero; 113 | sourceResolution.width = CGImageGetWidth(sourceImageRef); 114 | sourceResolution.height = CGImageGetHeight(sourceImageRef); 115 | float sourceTotalPixels = sourceResolution.width * sourceResolution.height; 116 | // Determine the scale ratio to apply to the input image 117 | // that results in an output image of the defined size. 118 | // see kDestImageSizeMB, and how it relates to destTotalPixels. 119 | float imageScale = kDestTotalPixels / sourceTotalPixels; 120 | CGSize destResolution = CGSizeZero; 121 | destResolution.width = (int)(sourceResolution.width*imageScale); 122 | destResolution.height = (int)(sourceResolution.height*imageScale); 123 | 124 | // current color space 125 | // 获取色彩空间 126 | CGColorSpaceRef colorspaceRef = [UIImage colorSpaceForImageRef:sourceImageRef]; 127 | 128 | size_t bytesPerRow = kBytesPerPixel * destResolution.width; 129 | 130 | // Allocate enough pixel data to hold the output image. 131 | void* destBitmapData = malloc( bytesPerRow * destResolution.height ); 132 | if (destBitmapData == NULL) { 133 | return image; 134 | } 135 | 136 | // kCGImageAlphaNone is not supported in CGBitmapContextCreate. 137 | // Since the original image here has no alpha info, use kCGImageAlphaNoneSkipLast 138 | // to create bitmap graphics contexts without alpha info. 139 | // 创建保存该图片信息的destContext 140 | destContext = CGBitmapContextCreate(destBitmapData, 141 | destResolution.width, 142 | destResolution.height, 143 | kBitsPerComponent, 144 | bytesPerRow, 145 | colorspaceRef, 146 | kCGBitmapByteOrderDefault|kCGImageAlphaNoneSkipLast); 147 | 148 | if (destContext == NULL) { 149 | free(destBitmapData); 150 | return image; 151 | } 152 | // 设置图片质量 153 | CGContextSetInterpolationQuality(destContext, kCGInterpolationHigh); 154 | 155 | // Now define the size of the rectangle to be used for the 156 | // incremental blits from the input image to the output image. 157 | // we use a source tile width equal to the width of the source 158 | // image due to the way that iOS retrieves image data from disk. 159 | // iOS must decode an image from disk in full width 'bands', even 160 | // if current graphics context is clipped to a subrect within that 161 | // band. Therefore we fully utilize all of the pixel data that results 162 | // from a decoding opertion by achnoring our tile size to the full 163 | // width of the input image. 164 | CGRect sourceTile = CGRectZero; 165 | sourceTile.size.width = sourceResolution.width;//图片宽度 166 | // The source tile height is dynamic. Since we specified the size 167 | // of the source tile in MB, see how many rows of pixels high it 168 | // can be given the input image width. 169 | sourceTile.size.height = (int)(kTileTotalPixels / sourceTile.size.width ); 170 | 171 | sourceTile.origin.x = 0.0f; 172 | // The output tile is the same proportions as the input tile, but 173 | // scaled to image scale. 174 | CGRect destTile; 175 | destTile.size.width = destResolution.width; 176 | destTile.size.height = sourceTile.size.height * imageScale;// 图片高度缩小后的高度 177 | destTile.origin.x = 0.0f; 178 | // The source seem overlap is proportionate to the destination seem overlap. 179 | // this is the amount of pixels to overlap each tile as we assemble the ouput image. 180 | float sourceSeemOverlap = (int)((kDestSeemOverlap/destResolution.height)*sourceResolution.height); 181 | CGImageRef sourceTileImageRef; 182 | // calculate the number of read/write operations required to assemble the 183 | // output image. 184 | int iterations = (int)( sourceResolution.height / sourceTile.size.height ); 185 | // If tile height doesn't divide the image height evenly, add another iteration 186 | // to account for the remaining pixels. 187 | int remainder = (int)sourceResolution.height % (int)sourceTile.size.height; 188 | if(remainder) { 189 | iterations++; 190 | } 191 | // Add seem overlaps to the tiles, but save the original tile height for y coordinate calculations. 192 | float sourceTileHeightMinusOverlap = sourceTile.size.height; 193 | sourceTile.size.height += sourceSeemOverlap; 194 | destTile.size.height += kDestSeemOverlap; 195 | for( int y = 0; y < iterations; ++y ) { 196 | @autoreleasepool { 197 | sourceTile.origin.y = y * sourceTileHeightMinusOverlap + sourceSeemOverlap; 198 | destTile.origin.y = destResolution.height - (( y + 1 ) * sourceTileHeightMinusOverlap * imageScale + kDestSeemOverlap); 199 | sourceTileImageRef = CGImageCreateWithImageInRect( sourceImageRef, sourceTile ); 200 | if( y == iterations - 1 && remainder ) { 201 | float dify = destTile.size.height; 202 | destTile.size.height = CGImageGetHeight( sourceTileImageRef ) * imageScale; 203 | dify -= destTile.size.height; 204 | destTile.origin.y += dify; 205 | } 206 | CGContextDrawImage( destContext, destTile, sourceTileImageRef ); 207 | CGImageRelease( sourceTileImageRef ); 208 | } 209 | } 210 | 211 | CGImageRef destImageRef = CGBitmapContextCreateImage(destContext); 212 | CGContextRelease(destContext); 213 | if (destImageRef == NULL) { 214 | return image; 215 | } 216 | UIImage *destImage = [UIImage imageWithCGImage:destImageRef scale:image.scale orientation:image.imageOrientation]; 217 | CGImageRelease(destImageRef); 218 | if (destImage == nil) { 219 | return image; 220 | } 221 | return destImage; 222 | } 223 | } 224 | 225 | + (BOOL)shouldDecodeImage:(nullable UIImage *)image { 226 | // Prevent "CGBitmapContextCreateImage: invalid context 0x0" error 227 | if (image == nil) { 228 | return NO; 229 | } 230 | 231 | // do not decode animated images 232 | // 动图无法解压 233 | if (image.images != nil) { 234 | return NO; 235 | } 236 | 237 | CGImageRef imageRef = image.CGImage; 238 | //获取透明信息 239 | CGImageAlphaInfo alpha = CGImageGetAlphaInfo(imageRef); 240 | BOOL anyAlpha = (alpha == kCGImageAlphaFirst || 241 | alpha == kCGImageAlphaLast || 242 | alpha == kCGImageAlphaPremultipliedFirst || 243 | alpha == kCGImageAlphaPremultipliedLast); 244 | // do not decode images with alpha 245 | // 包含透明信息的图片则不适合解压 246 | if (anyAlpha) { 247 | return NO; 248 | } 249 | 250 | return YES; 251 | } 252 | 253 | //是否需要缩小图像,若是像素超出限定的 kDestTotalPixels,那么需要缩小图像 254 | + (BOOL)shouldScaleDownImage:(nonnull UIImage *)image { 255 | BOOL shouldScaleDown = YES; 256 | 257 | CGImageRef sourceImageRef = image.CGImage; 258 | CGSize sourceResolution = CGSizeZero; 259 | sourceResolution.width = CGImageGetWidth(sourceImageRef); 260 | sourceResolution.height = CGImageGetHeight(sourceImageRef); 261 | float sourceTotalPixels = sourceResolution.width * sourceResolution.height; 262 | //图像的最大像素容量 / 特定图像的像素 263 | float imageScale = kDestTotalPixels / sourceTotalPixels; 264 | if (imageScale < 1) { 265 | //需要缩小 266 | shouldScaleDown = YES; 267 | } else { 268 | //不需要缩小 269 | shouldScaleDown = NO; 270 | } 271 | 272 | return shouldScaleDown; 273 | } 274 | 275 | + (CGColorSpaceRef)colorSpaceForImageRef:(CGImageRef)imageRef { 276 | // current 277 | // 色彩模型 278 | CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(CGImageGetColorSpace(imageRef)); 279 | // 获取色彩空间 280 | CGColorSpaceRef colorspaceRef = CGImageGetColorSpace(imageRef); 281 | // 不支持的色彩空间采用设备的RGB色彩空间 282 | BOOL unsupportedColorSpace = (imageColorSpaceModel == kCGColorSpaceModelUnknown || 283 | imageColorSpaceModel == kCGColorSpaceModelMonochrome || 284 | imageColorSpaceModel == kCGColorSpaceModelCMYK || 285 | imageColorSpaceModel == kCGColorSpaceModelIndexed); 286 | if (unsupportedColorSpace) { 287 | colorspaceRef = CGColorSpaceCreateDeviceRGB(); 288 | CFAutorelease(colorspaceRef); 289 | } 290 | return colorspaceRef; 291 | } 292 | #elif SD_MAC 293 | + (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image { 294 | return image; 295 | } 296 | 297 | + (nullable UIImage *)decodedAndScaledDownImageWithImage:(nullable UIImage *)image { 298 | return image; 299 | } 300 | #endif 301 | 302 | @end 303 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Downloader/SDWebImageDownloader.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDWebImageDownloader.h" 10 | #import "SDWebImageDownloaderOperation.h" 11 | #import 12 | 13 | @implementation SDWebImageDownloadToken 14 | @end 15 | 16 | 17 | @interface SDWebImageDownloader () 18 | 19 | @property (strong, nonatomic, nonnull) NSOperationQueue *downloadQueue; 20 | @property (weak, nonatomic, nullable) NSOperation *lastAddedOperation; 21 | @property (assign, nonatomic, nullable) Class operationClass; 22 | @property (strong, nonatomic, nonnull) NSMutableDictionary *URLOperations; 23 | @property (strong, nonatomic, nullable) SDHTTPHeadersMutableDictionary *HTTPHeaders; 24 | // This queue is used to serialize the handling of the network responses of all the download operation in a single queue 25 | @property (SDDispatchQueueSetterSementics, nonatomic, nullable) dispatch_queue_t barrierQueue; 26 | 27 | // The session in which data tasks will run 28 | @property (strong, nonatomic) NSURLSession *session; 29 | 30 | @end 31 | 32 | @implementation SDWebImageDownloader 33 | 34 | + (void)initialize { 35 | // Bind SDNetworkActivityIndicator if available (download it here: http://github.com/rs/SDNetworkActivityIndicator ) 36 | // To use it, just add #import "SDNetworkActivityIndicator.h" in addition to the SDWebImage import 37 | if (NSClassFromString(@"SDNetworkActivityIndicator")) { 38 | 39 | #pragma clang diagnostic push 40 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 41 | id activityIndicator = [NSClassFromString(@"SDNetworkActivityIndicator") performSelector:NSSelectorFromString(@"sharedActivityIndicator")]; 42 | #pragma clang diagnostic pop 43 | 44 | // Remove observer in case it was previously added. 45 | [[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:SDWebImageDownloadStartNotification object:nil]; 46 | [[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:SDWebImageDownloadStopNotification object:nil]; 47 | 48 | [[NSNotificationCenter defaultCenter] addObserver:activityIndicator 49 | selector:NSSelectorFromString(@"startActivity") 50 | name:SDWebImageDownloadStartNotification object:nil]; 51 | [[NSNotificationCenter defaultCenter] addObserver:activityIndicator 52 | selector:NSSelectorFromString(@"stopActivity") 53 | name:SDWebImageDownloadStopNotification object:nil]; 54 | } 55 | } 56 | 57 | + (nonnull instancetype)sharedDownloader { 58 | static dispatch_once_t once; 59 | static id instance; 60 | dispatch_once(&once, ^{ 61 | instance = [self new]; 62 | }); 63 | return instance; 64 | } 65 | 66 | - (nonnull instancetype)init { 67 | return [self initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; 68 | } 69 | 70 | - (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration { 71 | if ((self = [super init])) { 72 | _operationClass = [SDWebImageDownloaderOperation class]; 73 | _shouldDecompressImages = YES; 74 | _executionOrder = SDWebImageDownloaderFIFOExecutionOrder; 75 | _downloadQueue = [NSOperationQueue new]; 76 | _downloadQueue.maxConcurrentOperationCount = 6; 77 | _downloadQueue.name = @"com.hackemist.SDWebImageDownloader"; 78 | _URLOperations = [NSMutableDictionary new]; 79 | #ifdef SD_WEBP 80 | _HTTPHeaders = [@{@"Accept": @"image/webp,image/*;q=0.8"} mutableCopy]; 81 | #else 82 | _HTTPHeaders = [@{@"Accept": @"image/*;q=0.8"} mutableCopy]; 83 | #endif 84 | _barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderBarrierQueue", DISPATCH_QUEUE_CONCURRENT); 85 | _downloadTimeout = 15.0; 86 | 87 | sessionConfiguration.timeoutIntervalForRequest = _downloadTimeout; 88 | 89 | /** 90 | * Create the session for this task 91 | * We send nil as delegate queue so that the session creates a serial operation queue for performing all delegate 92 | * method calls and completion handler calls. 93 | */ 94 | self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration 95 | delegate:self 96 | delegateQueue:nil]; 97 | } 98 | return self; 99 | } 100 | 101 | - (void)dealloc { 102 | [self.session invalidateAndCancel]; 103 | self.session = nil; 104 | 105 | [self.downloadQueue cancelAllOperations]; 106 | SDDispatchQueueRelease(_barrierQueue); 107 | } 108 | 109 | - (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)field { 110 | if (value) { 111 | self.HTTPHeaders[field] = value; 112 | } 113 | else { 114 | [self.HTTPHeaders removeObjectForKey:field]; 115 | } 116 | } 117 | 118 | - (nullable NSString *)valueForHTTPHeaderField:(nullable NSString *)field { 119 | return self.HTTPHeaders[field]; 120 | } 121 | 122 | - (void)setMaxConcurrentDownloads:(NSInteger)maxConcurrentDownloads { 123 | _downloadQueue.maxConcurrentOperationCount = maxConcurrentDownloads; 124 | } 125 | 126 | - (NSUInteger)currentDownloadCount { 127 | return _downloadQueue.operationCount; 128 | } 129 | 130 | - (NSInteger)maxConcurrentDownloads { 131 | return _downloadQueue.maxConcurrentOperationCount; 132 | } 133 | 134 | - (void)setOperationClass:(nullable Class)operationClass { 135 | if (operationClass && [operationClass isSubclassOfClass:[NSOperation class]] && [operationClass conformsToProtocol:@protocol(SDWebImageDownloaderOperationInterface)]) { 136 | _operationClass = operationClass; 137 | } else { 138 | _operationClass = [SDWebImageDownloaderOperation class]; 139 | } 140 | } 141 | 142 | //传入图片的URL,图片下载过程的Block回调,图片完成的Block回调 143 | - (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url 144 | options:(SDWebImageDownloaderOptions)options 145 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 146 | completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock { 147 | __weak SDWebImageDownloader *wself = self; 148 | // 传入对应的参数,addProgressCallback: completedBlock: forURL: createCallback: 方法 149 | return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{ 150 | __strong __typeof (wself) sself = wself; 151 | //超时时间 默认15秒 152 | NSTimeInterval timeoutInterval = sself.downloadTimeout; 153 | if (timeoutInterval == 0.0) { 154 | timeoutInterval = 15.0; 155 | } 156 | 157 | // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise 158 | // Header的设置 159 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval]; 160 | request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies); 161 | request.HTTPShouldUsePipelining = YES; 162 | if (sself.headersFilter) { 163 | request.allHTTPHeaderFields = sself.headersFilter(url, [sself.HTTPHeaders copy]); 164 | } 165 | else { 166 | request.allHTTPHeaderFields = sself.HTTPHeaders; 167 | } 168 | //创建 SDWebImageDownloaderOperation,这个是下载任务的执行者。并设置对应的参数 169 | SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options]; 170 | operation.shouldDecompressImages = sself.shouldDecompressImages; 171 | //用于请求认证 172 | if (sself.urlCredential) { 173 | operation.credential = sself.urlCredential; 174 | } else if (sself.username && sself.password) { 175 | operation.credential = [NSURLCredential credentialWithUser:sself.username password:sself.password persistence:NSURLCredentialPersistenceForSession]; 176 | } 177 | //下载任务优先级 178 | if (options & SDWebImageDownloaderHighPriority) { 179 | operation.queuePriority = NSOperationQueuePriorityHigh; 180 | } else if (options & SDWebImageDownloaderLowPriority) { 181 | operation.queuePriority = NSOperationQueuePriorityLow; 182 | } 183 | //加入任务的执行队列 184 | [sself.downloadQueue addOperation:operation]; 185 | if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) { 186 | // Emulate LIFO execution order by systematically adding new operations as last operation's dependency 187 | [sself.lastAddedOperation addDependency:operation]; 188 | sself.lastAddedOperation = operation; 189 | } 190 | 191 | return operation; 192 | }]; 193 | } 194 | 195 | - (void)cancel:(nullable SDWebImageDownloadToken *)token { 196 | // dispatch_barrier_async 执行完上一个任务才执行, 并执行完这个任务才会执行下一个, 串行操作 197 | dispatch_barrier_async(self.barrierQueue, ^{ 198 | //根据token取出对应的SDWebImageDownloaderOperation对象然后调用该对象的cancel: 方法 199 | SDWebImageDownloaderOperation *operation = self.URLOperations[token.url]; 200 | BOOL canceled = [operation cancel:token.downloadOperationCancelToken]; 201 | if (canceled) { 202 | [self.URLOperations removeObjectForKey:token.url]; 203 | } 204 | }); 205 | } 206 | 207 | - (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock 208 | completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock 209 | forURL:(nullable NSURL *)url 210 | createCallback:(SDWebImageDownloaderOperation *(^)())createCallback { 211 | // The URL will be used as the key to the callbacks dictionary so it cannot be nil. If it is nil immediately call the completed block with no image or data. 212 | if (url == nil) { //url为nil block传回nil 213 | if (completedBlock != nil) { 214 | completedBlock(nil, nil, nil, NO); 215 | } 216 | return nil; 217 | } 218 | 219 | __block SDWebImageDownloadToken *token = nil; 220 | // dispatch_barrier_sync 是前面的任务结束后这个任务才执行, 这个执行完后下个才执行, 串行操作 221 | dispatch_barrier_sync(self.barrierQueue, ^{ 222 | SDWebImageDownloaderOperation *operation = self.URLOperations[url]; //根据url取下载操作 223 | if (!operation) { //如果url之前没有下载操作, 即第一次加载, 则 createCallback 224 | operation = createCallback(); 225 | self.URLOperations[url] = operation; 226 | 227 | __weak SDWebImageDownloaderOperation *woperation = operation; 228 | operation.completionBlock = ^{ 229 | SDWebImageDownloaderOperation *soperation = woperation; 230 | if (!soperation) return; 231 | if (self.URLOperations[url] == soperation) { 232 | [self.URLOperations removeObjectForKey:url]; 233 | }; 234 | }; 235 | } 236 | id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock]; 237 | 238 | token = [SDWebImageDownloadToken new]; 239 | token.url = url; 240 | token.downloadOperationCancelToken = downloadOperationCancelToken; 241 | }); 242 | 243 | return token; 244 | } 245 | 246 | - (void)setSuspended:(BOOL)suspended { 247 | (self.downloadQueue).suspended = suspended; 248 | } 249 | 250 | - (void)cancelAllDownloads { 251 | [self.downloadQueue cancelAllOperations]; 252 | } 253 | 254 | #pragma mark Helper methods 255 | //根据NSURLSessionTask的taskIdentifier取出对应的SDWebImageDownloaderOperation对象 256 | - (SDWebImageDownloaderOperation *)operationWithTask:(NSURLSessionTask *)task { 257 | SDWebImageDownloaderOperation *returnOperation = nil; 258 | for (SDWebImageDownloaderOperation *operation in self.downloadQueue.operations) { 259 | if (operation.dataTask.taskIdentifier == task.taskIdentifier) { 260 | returnOperation = operation; 261 | break; 262 | } 263 | } 264 | return returnOperation; 265 | } 266 | 267 | #pragma mark NSURLSessionDataDelegate 268 | 269 | - (void)URLSession:(NSURLSession *)session 270 | dataTask:(NSURLSessionDataTask *)dataTask 271 | didReceiveResponse:(NSURLResponse *)response 272 | completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler { 273 | 274 | // Identify the operation that runs this task and pass it the delegate method 275 | // 取得 SDWebImageDownloaderOperation 对象将URLSession的回调转发给它 276 | SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask]; 277 | 278 | [dataOperation URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler]; 279 | } 280 | 281 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { 282 | 283 | // Identify the operation that runs this task and pass it the delegate method 284 | SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask]; 285 | // 取得 SDWebImageDownloaderOperation 对象将URLSession的回调转发给它 286 | [dataOperation URLSession:session dataTask:dataTask didReceiveData:data]; 287 | } 288 | 289 | - (void)URLSession:(NSURLSession *)session 290 | dataTask:(NSURLSessionDataTask *)dataTask 291 | willCacheResponse:(NSCachedURLResponse *)proposedResponse 292 | completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler { 293 | 294 | // Identify the operation that runs this task and pass it the delegate method 295 | SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask]; 296 | // 取得 SDWebImageDownloaderOperation 对象将URLSession的回调转发给它 297 | [dataOperation URLSession:session dataTask:dataTask willCacheResponse:proposedResponse completionHandler:completionHandler]; 298 | } 299 | 300 | #pragma mark NSURLSessionTaskDelegate 301 | 302 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { 303 | // Identify the operation that runs this task and pass it the delegate method 304 | SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:task]; 305 | // 取得 SDWebImageDownloaderOperation 对象将URLSession的回调转发给它 306 | [dataOperation URLSession:session task:task didCompleteWithError:error]; 307 | } 308 | 309 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler { 310 | 311 | completionHandler(request); 312 | } 313 | 314 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { 315 | 316 | // Identify the operation that runs this task and pass it the delegate method 317 | SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:task]; 318 | // 取得 SDWebImageDownloaderOperation 对象将URLSession的回调转发给它 319 | [dataOperation URLSession:session task:task didReceiveChallenge:challenge completionHandler:completionHandler]; 320 | } 321 | 322 | @end 323 | -------------------------------------------------------------------------------- /SDWebImageComment/WebImage/Utils/SDWebImageManager.m: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the SDWebImage package. 3 | * (c) Olivier Poitrey 4 | * 5 | * For the full copyright and license information, please view the LICENSE 6 | * file that was distributed with this source code. 7 | */ 8 | 9 | #import "SDWebImageManager.h" 10 | #import 11 | #import "NSImage+WebCache.h" 12 | 13 | @interface SDWebImageCombinedOperation : NSObject 14 | 15 | @property (assign, nonatomic, getter = isCancelled) BOOL cancelled; 16 | @property (copy, nonatomic, nullable) SDWebImageNoParamsBlock cancelBlock; 17 | @property (strong, nonatomic, nullable) NSOperation *cacheOperation; 18 | 19 | @end 20 | 21 | @interface SDWebImageManager () 22 | 23 | @property (strong, nonatomic, readwrite, nonnull) SDImageCache *imageCache; 24 | @property (strong, nonatomic, readwrite, nonnull) SDWebImageDownloader *imageDownloader; 25 | @property (strong, nonatomic, nonnull) NSMutableSet *failedURLs; 26 | @property (strong, nonatomic, nonnull) NSMutableArray *runningOperations; 27 | 28 | @end 29 | 30 | @implementation SDWebImageManager 31 | 32 | + (nonnull instancetype)sharedManager { 33 | static dispatch_once_t once; 34 | static id instance; 35 | dispatch_once(&once, ^{ 36 | instance = [self new]; 37 | }); 38 | return instance; 39 | } 40 | 41 | - (nonnull instancetype)init { 42 | SDImageCache *cache = [SDImageCache sharedImageCache]; 43 | SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader]; 44 | return [self initWithCache:cache downloader:downloader]; 45 | } 46 | 47 | - (nonnull instancetype)initWithCache:(nonnull SDImageCache *)cache downloader:(nonnull SDWebImageDownloader *)downloader { 48 | if ((self = [super init])) { 49 | _imageCache = cache; 50 | _imageDownloader = downloader; 51 | _failedURLs = [NSMutableSet new]; 52 | _runningOperations = [NSMutableArray new]; 53 | } 54 | return self; 55 | } 56 | 57 | - (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url { 58 | if (!url) { 59 | return @""; 60 | } 61 | 62 | if (self.cacheKeyFilter) { 63 | return self.cacheKeyFilter(url); 64 | } else { 65 | return url.absoluteString; 66 | } 67 | } 68 | 69 | - (void)cachedImageExistsForURL:(nullable NSURL *)url 70 | completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock { 71 | NSString *key = [self cacheKeyForURL:url]; 72 | 73 | BOOL isInMemoryCache = ([self.imageCache imageFromMemoryCacheForKey:key] != nil); 74 | 75 | if (isInMemoryCache) { 76 | // making sure we call the completion block on the main queue 77 | dispatch_async(dispatch_get_main_queue(), ^{ 78 | if (completionBlock) { 79 | completionBlock(YES); 80 | } 81 | }); 82 | return; 83 | } 84 | 85 | [self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) { 86 | // the completion block of checkDiskCacheForImageWithKey:completion: is always called on the main queue, no need to further dispatch 87 | if (completionBlock) { 88 | completionBlock(isInDiskCache); 89 | } 90 | }]; 91 | } 92 | 93 | - (void)diskImageExistsForURL:(nullable NSURL *)url 94 | completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock { 95 | NSString *key = [self cacheKeyForURL:url]; 96 | 97 | [self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) { 98 | // the completion block of checkDiskCacheForImageWithKey:completion: is always called on the main queue, no need to further dispatch 99 | if (completionBlock) { 100 | completionBlock(isInDiskCache); 101 | } 102 | }]; 103 | } 104 | 105 | - (id )loadImageWithURL:(nullable NSURL *)url 106 | options:(SDWebImageOptions)options 107 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 108 | completed:(nullable SDInternalCompletionBlock)completedBlock { 109 | // Invoking this method without a completedBlock is pointless 110 | NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead"); 111 | 112 | // Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, XCode won't 113 | // throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString. 114 | if ([url isKindOfClass:NSString.class]) { 115 | url = [NSURL URLWithString:(NSString *)url]; 116 | } 117 | 118 | // Prevents app crashing on argument type error like sending NSNull instead of NSURL 119 | if (![url isKindOfClass:NSURL.class]) { //url格式有问题致nil 120 | url = nil; 121 | } 122 | 123 | __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new]; //new下载操作 124 | __weak SDWebImageCombinedOperation *weakOperation = operation; 125 | 126 | BOOL isFailedUrl = NO; 127 | if (url) { 128 | @synchronized (self.failedURLs) { 129 | isFailedUrl = [self.failedURLs containsObject:url]; //判断url是否是失效过的url 130 | } 131 | } 132 | //url长度为0 或 u设置了失败不再请求请求且url为失效url,返回error 133 | if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) { 134 | [self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil] url:url]; 135 | return operation; 136 | } 137 | 138 | @synchronized (self.runningOperations) { 139 | [self.runningOperations addObject:operation]; //添加下载任务 140 | } 141 | NSString *key = [self cacheKeyForURL:url]; //根据url生成唯一的key 142 | //缓存 143 | operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) { 144 | if (operation.isCancelled) { //判断操作是否取消, 若取消则将下载任务从下载队列中移除 145 | [self safelyRemoveOperationFromRunning:operation]; 146 | return; 147 | } 148 | 149 | if ((!cachedImage || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) { 150 | if (cachedImage && options & SDWebImageRefreshCached) { 151 | // If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image 152 | // AND try to re-download it in order to let a chance to NSURLCache to refresh it from server. 153 | [self callCompletionBlockForOperation:weakOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url]; 154 | } 155 | //下载图片 156 | // download if no image or requested to refresh anyway, and download allowed by delegate 157 | SDWebImageDownloaderOptions downloaderOptions = 0; 158 | if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority; 159 | if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload; 160 | if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache; 161 | if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground; 162 | if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies; 163 | if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates; 164 | if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority; 165 | if (options & SDWebImageScaleDownLargeImages) downloaderOptions |= SDWebImageDownloaderScaleDownLargeImages; 166 | 167 | if (cachedImage && options & SDWebImageRefreshCached) { 168 | // force progressive off if image already cached but forced refreshing 169 | downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload; 170 | // ignore image read from NSURLCache if image if cached but force refreshing 171 | downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse; 172 | } 173 | 174 | SDWebImageDownloadToken *subOperationToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) { 175 | __strong __typeof(weakOperation) strongOperation = weakOperation; 176 | if (!strongOperation || strongOperation.isCancelled) { 177 | // Do nothing if the operation was cancelled 178 | // See #699 for more details 179 | // if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data 180 | } else if (error) { 181 | [self callCompletionBlockForOperation:strongOperation completion:completedBlock error:error url:url]; 182 | 183 | if ( error.code != NSURLErrorNotConnectedToInternet 184 | && error.code != NSURLErrorCancelled 185 | && error.code != NSURLErrorTimedOut 186 | && error.code != NSURLErrorInternationalRoamingOff 187 | && error.code != NSURLErrorDataNotAllowed 188 | && error.code != NSURLErrorCannotFindHost 189 | && error.code != NSURLErrorCannotConnectToHost) { 190 | @synchronized (self.failedURLs) { //请求失败, 将url加入无效url列表 191 | [self.failedURLs addObject:url]; 192 | } 193 | } 194 | } 195 | else { 196 | if ((options & SDWebImageRetryFailed)) { 197 | @synchronized (self.failedURLs) { 198 | [self.failedURLs removeObject:url]; 199 | } 200 | } 201 | 202 | BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly); 203 | 204 | if (options & SDWebImageRefreshCached && cachedImage && !downloadedImage) { 205 | // Image refresh hit the NSURLCache cache, do not call the completion block 206 | } else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) { 207 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 208 | UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url]; 209 | 210 | if (transformedImage && finished) { 211 | BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage]; 212 | // pass nil if the image was transformed, so we can recalculate the data from the image 213 | [self.imageCache storeImage:transformedImage imageData:(imageWasTransformed ? nil : downloadedData) forKey:key toDisk:cacheOnDisk completion:nil]; 214 | } 215 | 216 | [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url]; 217 | }); 218 | } else { 219 | if (downloadedImage && finished) { 220 | [self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key toDisk:cacheOnDisk completion:nil]; 221 | } 222 | [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:downloadedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url]; 223 | } 224 | } 225 | 226 | if (finished) { 227 | [self safelyRemoveOperationFromRunning:strongOperation]; 228 | } 229 | }]; 230 | operation.cancelBlock = ^{ 231 | [self.imageDownloader cancel:subOperationToken]; 232 | __strong __typeof(weakOperation) strongOperation = weakOperation; 233 | [self safelyRemoveOperationFromRunning:strongOperation]; 234 | }; 235 | } else if (cachedImage) { 236 | __strong __typeof(weakOperation) strongOperation = weakOperation; 237 | [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url]; 238 | [self safelyRemoveOperationFromRunning:operation]; 239 | } else { 240 | // Image not in cache and download disallowed by delegate 241 | __strong __typeof(weakOperation) strongOperation = weakOperation; 242 | [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url]; 243 | [self safelyRemoveOperationFromRunning:operation]; 244 | } 245 | }]; 246 | 247 | return operation; 248 | } 249 | 250 | - (void)saveImageToCache:(nullable UIImage *)image forURL:(nullable NSURL *)url { 251 | if (image && url) { 252 | NSString *key = [self cacheKeyForURL:url]; 253 | [self.imageCache storeImage:image forKey:key toDisk:YES completion:nil]; 254 | } 255 | } 256 | 257 | - (void)cancelAll { 258 | @synchronized (self.runningOperations) { 259 | NSArray *copiedOperations = [self.runningOperations copy]; 260 | [copiedOperations makeObjectsPerformSelector:@selector(cancel)]; 261 | [self.runningOperations removeObjectsInArray:copiedOperations]; 262 | } 263 | } 264 | 265 | - (BOOL)isRunning { 266 | BOOL isRunning = NO; 267 | @synchronized (self.runningOperations) { 268 | isRunning = (self.runningOperations.count > 0); 269 | } 270 | return isRunning; 271 | } 272 | 273 | - (void)safelyRemoveOperationFromRunning:(nullable SDWebImageCombinedOperation*)operation { 274 | @synchronized (self.runningOperations) { 275 | if (operation) { 276 | [self.runningOperations removeObject:operation]; 277 | } 278 | } 279 | } 280 | 281 | - (void)callCompletionBlockForOperation:(nullable SDWebImageCombinedOperation*)operation 282 | completion:(nullable SDInternalCompletionBlock)completionBlock 283 | error:(nullable NSError *)error 284 | url:(nullable NSURL *)url { 285 | [self callCompletionBlockForOperation:operation completion:completionBlock image:nil data:nil error:error cacheType:SDImageCacheTypeNone finished:YES url:url]; 286 | } 287 | 288 | - (void)callCompletionBlockForOperation:(nullable SDWebImageCombinedOperation*)operation 289 | completion:(nullable SDInternalCompletionBlock)completionBlock 290 | image:(nullable UIImage *)image 291 | data:(nullable NSData *)data 292 | error:(nullable NSError *)error 293 | cacheType:(SDImageCacheType)cacheType 294 | finished:(BOOL)finished 295 | url:(nullable NSURL *)url { 296 | dispatch_main_async_safe(^{ 297 | if (operation && !operation.isCancelled && completionBlock) { 298 | completionBlock(image, data, error, cacheType, finished, url); 299 | } 300 | }); 301 | } 302 | 303 | @end 304 | 305 | 306 | @implementation SDWebImageCombinedOperation 307 | 308 | - (void)setCancelBlock:(nullable SDWebImageNoParamsBlock)cancelBlock { 309 | // check if the operation is already cancelled, then we just call the cancelBlock 310 | if (self.isCancelled) { 311 | if (cancelBlock) { 312 | cancelBlock(); 313 | } 314 | _cancelBlock = nil; // don't forget to nil the cancelBlock, otherwise we will get crashes 315 | } else { 316 | _cancelBlock = [cancelBlock copy]; 317 | } 318 | } 319 | 320 | - (void)cancel { 321 | self.cancelled = YES; 322 | if (self.cacheOperation) { 323 | [self.cacheOperation cancel]; 324 | self.cacheOperation = nil; 325 | } 326 | if (self.cancelBlock) { 327 | self.cancelBlock(); 328 | 329 | // TODO: this is a temporary fix to #809. 330 | // Until we can figure the exact cause of the crash, going with the ivar instead of the setter 331 | // self.cancelBlock = nil; 332 | _cancelBlock = nil; 333 | } 334 | } 335 | 336 | @end 337 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![SDWebImage](https://raw.githubusercontent.com/rs/SDWebImage/master/SDWebImage_logo.png) 2 | 3 | >在iOS项目常用的框架中,**[SDWebImage](https://github.com/rs/SDWebImage)** 是不可少的, 相信大部分的iOS 开发攻城狮都和`SDWebImage`打过交道, 使用是使用了,但是你了解阅读过它的源码,了解它的实现原理吗? 4 | 5 | >这两天闲下来, 我有时间阅读了`SDWebImage`的源码, 收获很大,为了整理下我理解的思路, 把它变成自己的知识,就写了这篇文章,向大家分享下我的阅读心得。 6 | 7 | - ### SDWebImage简介 8 | 1. 提供 `UIImageView`, `UIButton`, `MKAnnotationView` 的分类,用来加载网络图片,并进行缓存管理; 9 | 2. **异步**方式来下载网络图片 10 | 3. 异步方式: `memory` (内存)+ `disk` (磁盘) 来缓存网络图片,自动管理缓存; 11 | 4. 后台图片解码,转换及压缩; 12 | 5. 同一个 **URL** 不会重复下载; 13 | 6. 失效的 URL 不会被无限重试; 14 | 7. 支持 **GIF动画** 及 **WebP** 格式; 15 | 8. 开启 *子线程* 进行耗时操作,不阻塞主线程; 16 | 9. 使用 **GCD** 和 **ARC**; 17 | 18 | - ### SDWebImage使用 19 | 1. `UIImageView`加载图片: 20 | ``` 21 | image_Icon.sd_setImage(with: URL(string: ""), placeholderImage: UIImage(named: "")) 22 | ``` 23 | 2. 可使用 **闭包** 获取图片下载进度及成功失败状态: 24 | ``` 25 | image_Icon.sd_setImage(with: URL.init(string: ""), placeholderImage: UIImage.init(named: ""), options: .avoidAutoSetImage) { (image, error, cacheType, url) in 26 | //image:加载的图片, error:加载错误, cacheType: 缓存类型, url:图片url 27 | } 28 | ``` 29 | 3. `SDWebImageManager`下载图片 30 | ``` 31 | let manager = SDWebImageManager.shared() 32 | manager?.downloadImage(with: URL.init(string: ""), options: .avoidAutoSetImage, progress: { (receivedSize, expectedSize) in 33 | //receivedSize:接受大小,expectedSize:总大小 34 | }, completed: { (image, error, cacheType, isFinished, url) in 35 | //image:加载的图片,error:错误,cacheType:缓存类型,isFinished是否加载完成,url: 图片url 36 | }) 37 | ``` 38 | 4. `SDWebImageDownloader` 下载图片 39 | 我们如果遇到需要单独下载图片, 可使用 `SDWebImageDownloader` ,来下载图片, 但下载的图片 **不缓存** . 40 | ``` 41 | let downLoader = SDWebImageDownloader.shared() 42 | downLoader?.downloadImage(with: URL.init(string: ""), options: .useNSURLCache, progress: { (receivedSize, expectedSize) in 43 | //receivedSize:接受大小,expectedSize:总大小 44 | }, completed: { (image, data, error, isFinished) in 45 | //data: 图片解码后数据 46 | if (image != nil) && isFinished{ 47 | //使用图片 48 | } 49 | }) 50 | ``` 51 | 5. `SDImageCache` 缓存图片 52 | 如果需要缓存图片, 可使用 `SDImageCache `, `SDImageCache`支持内存缓存和异步的磁盘缓存. 53 | - 添加缓存(默认 **同时缓存到内存和磁盘** 中) 54 | ``` 55 | SDImageCache.shared().store(UIImage.init(named: ""), forKey: "") 56 | ``` 57 | - 也可设置只缓存内存,不缓存磁盘: 58 | ``` 59 | SDImageCache.shared().store(UIImage.init(named: ""), forKey: "", toDisk: false) 60 | ``` 61 | - 可通过key读取缓存,key默认是图片的url 62 | ``` 63 | SDImageCache.shared().queryDiskCache(forKey: "") { (image, cacheType) in 64 | 65 | } 66 | ``` 67 | 6. `UIButton` 加载图片 68 | `UIButton` 加载图片,可以区分状态,如 normal, selected, highlighted等 69 | ``` 70 | btn.sd_setImage(with: URL.init(string: ""), for: .normal, placeholderImage: UIImage.init(named: "placeHolderImage")) 71 | btn.sd_setImage(with: URL.init(string: ""), for: .highlighted, placeholderImage: UIImage.init(named: "placeHolderImage")) 72 | ``` 73 | 74 | - ### SDWebImage 架构图及流程图 75 | 76 | 1. `SDWebImage`调用只需简单的几行代码,对攻城狮来说十分便捷,这得益与代码的整体架构,整体机构图如下: 77 | ![](https://raw.githubusercontent.com/rs/SDWebImage/master/Docs/SDWebImageClassDiagram.png) 78 | 79 | 2. `SDWebImage` 核心在于 **下载,缓存及显示** 图片,这个流程如下: 80 | ![](https://raw.githubusercontent.com/rs/SDWebImage/master/Docs/SDWebImageSequenceDiagram.png) 81 | 82 | 注: 强烈建议好好看下上面的流程图, 对理解 `SDWebImage` 非常有帮助. 83 | 84 | 3. 类功能列表 85 | `SDWebImage` 类区分的很清晰, 各个类功能也很明确, 各司其职, 封装的特别好. 86 | 87 | | 类 | 功能 | 88 | | ------------------------------- | ----------------------------- | 89 | | `SDWebImageManager` | 将图片下载`SDWebImageDownloader` 和图片缓存`SDImageCache` 两个独立的功能组合起来 | 90 | | `SDWebImageDownloader` | 专门用来下载图片和优化图片加载的,跟缓存没有关系 | 91 | | `SDWebImageDownloaderOperation` | 继承于 `NSOperation`,用来处理下载任务 | 92 | | `SDImageCache` | 异步处理内存缓存和磁盘缓存,不阻塞主线程 | 93 | | `SDWebImageDecoder ` | 图片解码器,用于图片下载完成后进行解码 | 94 | | `SDWebImagePrefetcher ` | 预下载图片,方便后续使用,图片下载的优先级低,其内部由 `SDWebImageManager` 来处理图片下载和缓存 | 95 | | `UIView+WebCacheOperation ` | 图片加载operation,可取消和移除,可显示加载进度view | 96 | | `UIImageView+WebCache ` | 集成 `SDWebImageManager` 的图片下载和缓存功能到 `UIImageView` 的方法中,方便通过 `UIImageView` 直接调用 | 97 | | `UIImageView+HighlightedWebCache ` | 和 `UIImageView+WebCache` 功能相似,用于加载 **highlighted** 状态的图片 | 98 | | `UIButton+WebCache` | 集成 `SDWebImageManager` 的图片下载和缓存功能到 `UIImageView` 的方法中,方便通过 `UIButton` 直接调用 | 99 | | `MKAnnotationView+WebCache` | 集成 `SDWebImageManager` 的图片下载和缓存功能到 `MKAnnotationView` 的方法中,方便通过 `MKAnnotationView` 直接调用 | 100 | | `NSData+ImageContentType` | 用于获取图片数据的格式(如:JPEG、PNG、GIF等) | 101 | | `UIImage+GIF` | 用于加载 **GIF** 动图,还可判断图片是否是GIF格式 | 102 | | `UIImage+WebP` | 用于加载 **WebP** 图片 | 103 | | `UIImage+MultiFormat` | 根据不同格式的二进制数据转成 `UIImage` 对象 | 104 | 105 | 106 | - ### SDWebImage 源码解读 107 | `SDWebImage` 功能强大, 核心是图片的下载,缓存等操作, 下载又有预下载,url失效等细节,缓存有内存,磁盘缓存, 源码很详细,因为篇幅有限,这里我详细介绍下 `SDWebImage` 的核心逻辑, 也就是从下载到缓存,最后显示的流程. 108 | 109 | >注: 强烈建议先下载我的带备注源码, 结合源码来理解, 这样能更好的学习!!! 110 | 111 | 以 `UIImageView`为例: 112 | 1. `UIImageView+WebCache` 提供了多个加载图片的方法: 113 | ``` 114 | -(void)sd_setImageWithURL:(nullable NSURL *)url; //之传入图片url 115 | - (void)sd_setImageWithURL:(nullable NSURL *)url 116 | placeholderImage:(nullable UIImage *)placeholder; //可设置占位图 117 | - (void)sd_setImageWithURL:(nullable NSURL *)url 118 | placeholderImage:(nullable UIImage *)placeholder 119 | options:(SDWebImageOptions)options; //可设置占位图和加载方式 120 | - (void)sd_setImageWithURL:(nullable NSURL *)url 121 | completed:(nullable SDExternalCompletionBlock)completedBlock; // 122 | - (void)sd_setImageWithURL:(nullable NSURL *)url 123 | placeholderImage:(nullable UIImage *)placeholder 124 | completed:(nullable SDExternalCompletionBlock)completedBlock; // 在完成的闭包中可获取到 加载的image error 缓存方式 及图片url 125 | - (void)sd_setImageWithURL:(nullable NSURL *)url 126 | placeholderImage:(nullable UIImage *)placeholder 127 | options:(SDWebImageOptions)options 128 | completed:(nullable SDExternalCompletionBlock)completedBlock; 129 | - (void)sd_setImageWithURL:(nullable NSURL *)url 130 | placeholderImage:(nullable UIImage *)placeholder 131 | options:(SDWebImageOptions)options 132 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 133 | completed:(nullable SDExternalCompletionBlock)completedBlock; //可获取到下载进度 134 | - (void)sd_setImageWithPreviousCachedImageWithURL:(nullable NSURL *)url 135 | placeholderImage:(nullable UIImage *)placeholder 136 | options:(SDWebImageOptions)options 137 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 138 | completed:(nullable SDExternalCompletionBlock)completedBlock; 139 | ``` 140 | 这几个方法其实内部最后调用的是一个方法,这个方法封装在 `UIView+WebCache` 中,具体代码加注释如下: 141 | ``` 142 | // 所有加载图片操作都调用这个方法 143 | - (void)sd_internalSetImageWithURL:(nullable NSURL *)url 144 | placeholderImage:(nullable UIImage *)placeholder 145 | options:(SDWebImageOptions)options 146 | operationKey:(nullable NSString *)operationKey 147 | setImageBlock:(nullable SDSetImageBlock)setImageBlock 148 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 149 | completed:(nullable SDExternalCompletionBlock)completedBlock { 150 | NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]); 151 | [self sd_cancelImageLoadOperationWithKey:validOperationKey]; //取消之前的下载任务 152 | objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC); //利用runtime动态添加属性 153 | 154 | if (!(options & SDWebImageDelayPlaceholder)) { 155 | //如果模式不是 SDWebImageDelayPlaceholder, 则先设置占位图 156 | dispatch_main_async_safe(^{ 157 | [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock]; 158 | }); 159 | } 160 | 161 | //url不为nil 162 | if (url) { 163 | // check if activityView is enabled or not 164 | if ([self sd_showActivityIndicatorView]) { 165 | [self sd_addActivityIndicator]; //根据设置判断是否显示进度条 166 | } 167 | 168 | __weak __typeof(self)wself = self; 169 | id operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { 170 | __strong __typeof (wself) sself = wself; 171 | [sself sd_removeActivityIndicator]; //移除进度条 172 | if (!sself) { 173 | return; //如果self不存在 及已经被释放则return 174 | } 175 | dispatch_main_async_safe(^{ 176 | if (!sself) { 177 | return; 178 | } 179 | if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) { 180 | // 设置的不自动设置图片, 则通过block返回image 181 | completedBlock(image, error, cacheType, url); 182 | return; 183 | } else if (image) { 184 | //设置图片 185 | [sself sd_setImage:image imageData:data basedOnClassOrViaCustomSetImageBlock:setImageBlock]; 186 | [sself sd_setNeedsLayout]; 187 | } else { 188 | if ((options & SDWebImageDelayPlaceholder)) { 189 | // image下载不成功则设置占位图 190 | [sself sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock]; 191 | [sself sd_setNeedsLayout]; 192 | } 193 | } 194 | if (completedBlock && finished) { 195 | completedBlock(image, error, cacheType, url); 196 | } 197 | }); 198 | }]; 199 | [self sd_setImageLoadOperation:operation forKey:validOperationKey]; 200 | } else { 201 | //url 为nil, block返回error 202 | dispatch_main_async_safe(^{ 203 | [self sd_removeActivityIndicator]; 204 | if (completedBlock) { 205 | NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}]; 206 | completedBlock(nil, error, SDImageCacheTypeNone, url); 207 | } 208 | }); 209 | } 210 | } 211 | ``` 212 | 213 | 2. 看上面的代码,在加载图片第一步,会先把这个 `UIImageView` 动态添加的下载操作取消, 如果之前这个 `UIImageView` 没有添加过这个属性则不进行这个操作,这么做的原因是: `SDWebImage` 将图片对应的下载操作放到 `UIView` 的一个自定义字典属性 `operationDictionary` 中,取消下载操作第一步也是从这个 UIView 的自定义字典属性 `operationDictionary` 中取出所有的下载操作,然后依次调用取消方法,最后将取消的操作从 `operationDictionary)` 字典属性中移除。具体代码+注释如下: 214 | ``` 215 | // 取消图片加载操作 216 | - (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key { 217 | // Cancel in progress downloader from queue 218 | SDOperationsDictionary *operationDictionary = [self operationDictionary]; //获取UIView上动态添加的属性 219 | id operations = operationDictionary[key]; 220 | if (operations) { //如果有对应的加载操作 221 | if ([operations isKindOfClass:[NSArray class]]) { 222 | // SDWebImageOperation数组, 将数组中的每个加载操作都取消 223 | for (id operation in operations) { 224 | if (operation) { 225 | [operation cancel]; 226 | } 227 | } 228 | } else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){ 229 | //实现 SDWebImageOperation 协议 230 | [(id) operations cancel]; 231 | } 232 | [operationDictionary removeObjectForKey:key]; //取消后 移除这个属性 233 | } 234 | } 235 | ``` 236 | 237 | 3. 移除之前没用的图片下载操作之后就创建一个新的图片下载操作,操作完成后讲操作设置到 `UIView` 的自定义字典属性 `operationDictionary` 中。这个操作是在 1 步骤中, 具体代码如下: 238 | ``` 239 | objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC); //利用runtime动态添加属性 240 | /// 方法中具体加载操作... 241 | [self sd_setImageLoadOperation:operation forKey:validOperationKey]; //给添加的属性赋值 242 | ``` 243 | 4. 添加了新的下载操作, 点进来看下具体的代码, 发现先判断了url的有效性, `SDWebImage` 保存有一个失效的url列表,如果url请求失败了会加入这个列表,保证不重复请求失效的url. 244 | 先点击下载操作查看下下载代码: 245 | ``` 246 | id operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { 247 | ``` 248 | 点进来后解读具体代码: 249 | ``` 250 | // Prevents app crashing on argument type error like sending NSNull instead of NSURL 251 | if (![url isKindOfClass:NSURL.class]) { //url格式有问题致nil 252 | url = nil; 253 | } 254 | 255 | __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new]; //new下载操作 256 | __weak SDWebImageCombinedOperation *weakOperation = operation; 257 | 258 | BOOL isFailedUrl = NO; 259 | if (url) { 260 | @synchronized (self.failedURLs) { 261 | isFailedUrl = [self.failedURLs containsObject:url]; //判断url是否是失效过的url 262 | } 263 | } 264 | //url长度为0 或 u设置了失败不再请求请求且url为失效url,返回error 265 | if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) { 266 | [self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil] url:url]; 267 | return operation; 268 | } 269 | ``` 270 | 5. url没问题,则开始下载任务, 在下载之前,首先会根据图片的URL生成唯一的key,用来查找内存的磁盘中的缓存. 271 | ``` 272 | NSString *key = [self cacheKeyForURL:url]; //根据url生成唯一的key 273 | //缓存 274 | operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) { 275 | ``` 276 | 点击查看查找缓存的具体代码, 首先会先根据key请求内存缓存, 若存在则block传回缓存图片, 若没有则开辟子线程(异步不阻塞主线程)查找磁盘中缓存, 若在磁盘中查找到,会将缓存数据在内存中也缓存份,然后block传回缓存数据. 277 | ``` 278 | - (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock { 279 | if (!key) { //key为nil return nil 280 | if (doneBlock) { 281 | doneBlock(nil, nil, SDImageCacheTypeNone); 282 | } 283 | return nil; 284 | } 285 | 286 | // First check the in-memory cache... 先根据key查找内存缓存 287 | UIImage *image = [self imageFromMemoryCacheForKey:key]; 288 | if (image) { 289 | NSData *diskData = nil; 290 | if ([image isGIF]) { //是否是GIF图片 291 | diskData = [self diskImageDataBySearchingAllPathsForKey:key]; 292 | } 293 | if (doneBlock) { //将内存缓存中找到的图片返回 294 | doneBlock(image, diskData, SDImageCacheTypeMemory); 295 | } 296 | return nil; 297 | } 298 | 299 | NSOperation *operation = [NSOperation new]; //这里new operation,是为了使用NSOperation的取消方法,通过设置取消方法来达到取消异步从磁盘中读取缓存的操作 300 | //开子线程 查找磁盘中的缓存 301 | dispatch_async(self.ioQueue, ^{ 302 | if (operation.isCancelled) { //操作取消的话 直接return 303 | // do not call the completion if cancelled 304 | return; 305 | } 306 | 307 | @autoreleasepool { //根据key查找磁盘缓存 308 | NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key]; 309 | UIImage *diskImage = [self diskImageForKey:key]; 310 | if (diskImage && self.config.shouldCacheImagesInMemory) { 311 | NSUInteger cost = SDCacheCostForImage(diskImage); //在磁盘中查找到会在内存中也缓存份 312 | [self.memCache setObject:diskImage forKey:key cost:cost]; 313 | } 314 | 315 | if (doneBlock) { 316 | dispatch_async(dispatch_get_main_queue(), ^{ 317 | doneBlock(diskImage, diskData, SDImageCacheTypeDisk); 318 | }); 319 | } 320 | } 321 | }); 322 | 323 | return operation; 324 | } 325 | ``` 326 | 6. 如果内存和磁盘都没有读取到缓存,则进行下载操作. 327 | ``` 328 | SDWebImageDownloadToken *subOperationToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) { 329 | ``` 330 | 点进去查看具体的下载代码,设置请求超时时间, 然后创建 `NSMutableURLRequest` 用于请求, 然后新建一个 `SDWebImageDownloaderOperation` 下载任务, 设置参数后, 讲下载任务加入下载队列下载. 331 | ``` 332 | //传入图片的URL,图片下载过程的Block回调,图片完成的Block回调 333 | - (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url 334 | options:(SDWebImageDownloaderOptions)options 335 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 336 | completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock { 337 | __weak SDWebImageDownloader *wself = self; 338 | // 传入对应的参数,addProgressCallback: completedBlock: forURL: createCallback: 方法 339 | return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{ 340 | __strong __typeof (wself) sself = wself; 341 | //超时时间 默认15秒 342 | NSTimeInterval timeoutInterval = sself.downloadTimeout; 343 | if (timeoutInterval == 0.0) { 344 | timeoutInterval = 15.0; 345 | } 346 | 347 | // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise 348 | // Header的设置 349 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval]; 350 | request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies); 351 | request.HTTPShouldUsePipelining = YES; 352 | if (sself.headersFilter) { 353 | request.allHTTPHeaderFields = sself.headersFilter(url, [sself.HTTPHeaders copy]); 354 | } 355 | else { 356 | request.allHTTPHeaderFields = sself.HTTPHeaders; 357 | } 358 | //创建 SDWebImageDownloaderOperation,这个是下载任务的执行者。并设置对应的参数 359 | SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options]; 360 | operation.shouldDecompressImages = sself.shouldDecompressImages; 361 | //用于请求认证 362 | if (sself.urlCredential) { 363 | operation.credential = sself.urlCredential; 364 | } else if (sself.username && sself.password) { 365 | operation.credential = [NSURLCredential credentialWithUser:sself.username password:sself.password persistence:NSURLCredentialPersistenceForSession]; 366 | } 367 | //下载任务优先级 368 | if (options & SDWebImageDownloaderHighPriority) { 369 | operation.queuePriority = NSOperationQueuePriorityHigh; 370 | } else if (options & SDWebImageDownloaderLowPriority) { 371 | operation.queuePriority = NSOperationQueuePriorityLow; 372 | } 373 | //加入任务的执行队列 374 | [sself.downloadQueue addOperation:operation]; 375 | if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) { 376 | // Emulate LIFO execution order by systematically adding new operations as last operation's dependency 377 | [sself.lastAddedOperation addDependency:operation]; 378 | sself.lastAddedOperation = operation; 379 | } 380 | 381 | return operation; 382 | }]; 383 | } 384 | ``` 385 | 7. 如果该 url 是第一次加载的话,那么就会执行 createCallback 这个回调block ,然后在 createCallback 里面开始构建网络请求,在下载过程中执行各类进度 block 回调. 386 | ``` 387 | - (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock 388 | completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock 389 | forURL:(nullable NSURL *)url 390 | createCallback:(SDWebImageDownloaderOperation *(^)())createCallback { 391 | // The URL will be used as the key to the callbacks dictionary so it cannot be nil. If it is nil immediately call the completed block with no image or data. 392 | if (url == nil) { //url为nil block传回nil 393 | if (completedBlock != nil) { 394 | completedBlock(nil, nil, nil, NO); 395 | } 396 | return nil; 397 | } 398 | 399 | __block SDWebImageDownloadToken *token = nil; 400 | // dispatch_barrier_sync 是前面的任务结束后这个任务才执行, 这个执行完后下个才执行, 串行操作 401 | dispatch_barrier_sync(self.barrierQueue, ^{ 402 | SDWebImageDownloaderOperation *operation = self.URLOperations[url]; //根据url取下载操作 403 | if (!operation) { //如果url之前没有下载操作, 即第一次加载, 则 createCallback 404 | operation = createCallback(); 405 | self.URLOperations[url] = operation; 406 | 407 | __weak SDWebImageDownloaderOperation *woperation = operation; 408 | operation.completionBlock = ^{ 409 | SDWebImageDownloaderOperation *soperation = woperation; 410 | if (!soperation) return; 411 | if (self.URLOperations[url] == soperation) { 412 | [self.URLOperations removeObjectForKey:url]; 413 | }; 414 | }; 415 | } 416 | id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock]; 417 | 418 | token = [SDWebImageDownloadToken new]; 419 | token.url = url; 420 | token.downloadOperationCancelToken = downloadOperationCancelToken; 421 | }); 422 | 423 | return token; 424 | } 425 | ``` 426 | 8. 图片下载完成后, 会回到完成的的 block 回调中做图片转换处理和缓存操作. 427 | ![转换和缓存](https://upload-images.jianshu.io/upload_images/8283020-5b8ff1ea222d8cb4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 428 | 429 | 9. 下载和解码缓存后, 就只剩最后一步,即回到` UIImageView` 控件的设置图片方法 block 回调中,给对应的 `UIImageView` 设置图片, 整个加载流程到这里就完成了. 430 | ``` 431 | dispatch_main_async_safe(^{ 432 | if (!sself) { 433 | return; 434 | } 435 | if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) { 436 | // 设置的不自动设置图片, 则通过block返回image 437 | completedBlock(image, error, cacheType, url); 438 | return; 439 | } else if (image) { 440 | //设置图片 441 | [sself sd_setImage:image imageData:data basedOnClassOrViaCustomSetImageBlock:setImageBlock]; 442 | [sself sd_setNeedsLayout]; 443 | } else { 444 | if ((options & SDWebImageDelayPlaceholder)) { 445 | // image下载不成功则设置占位图 446 | [sself sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock]; 447 | [sself sd_setNeedsLayout]; 448 | } 449 | } 450 | if (completedBlock && finished) { 451 | completedBlock(image, error, cacheType, url); 452 | } 453 | }); 454 | }]; 455 | ``` 456 | 457 | - ### `SDWebImage` 使用中常见问题 458 | 1. 使用 `UITableViewCell` 中的` imageView` 加载不同尺寸的网络图片时会出现尺寸缩放问题. 459 | >解决方案:可以自定义 `UITableViewCell`,重写 `-layoutSubviews` 方法,调整位置尺寸;或者直接弃用 `UITableViewCell` 的 `imageView`,自己添加一个 `imageView` 作为子控件。 460 | 2. `SDWebImage` 在进行缓存时忽略了所有服务器返回的 `caching control` 设置,并且在缓存时没有做时间限制,这也就意味着图片 **URL** 必须是静态的了,要求服务器上一个 **URL** 对应的图片内容不允许更新。但是如果存储图片的服务器不由自己控制,也就是说 图片内容更新了,**URL** 却没有更新,这种情况怎么办? 461 | > 解决方案:在调用 `sd_setImageWithURL: placeholderImage: options:` 方法时设置 `options` 参数为 `SDWebImageRefreshCached`,这样虽然会降低性能,但是下载图片时会照顾到服务器返回的 `caching control`。 462 | 3. 在加载图片时,如何添加默认的 `progress indicator` ? 463 | >解决方案:在调用 `-sd_setImageWithURL:` 方法之前,先调用展示进度条方法, 完成后不需要管, 因为 `SDWebImage`里面完成后已经对进度条进行隐藏. 464 | ``` 465 | /** 466 | * Show activity UIActivityIndicatorView 467 | */ 468 | - (void)sd_setShowActivityIndicatorView:(BOOL)show; 469 | 470 | * set desired UIActivityIndicatorViewStyle 471 | * 472 | * @param style The style of the UIActivityIndicatorView 473 | */ 474 | - (void)sd_setIndicatorStyle:(UIActivityIndicatorViewStyle)style; 475 | ``` 476 | 4. `SDWebImage` 在加载图片网络请求的 `NSURLConnection` 的代理中对httpCode 做了判断,当 httpCode 为 304 的时候放弃下载,读取缓存,如果遇到304请求需要请求的课修改代码, 具体代码如下: 477 | ![](https://upload-images.jianshu.io/upload_images/8283020-17512971f62582af.png) 478 | 479 | - ### 总结 480 | `SDWebImage` 是一个非常好的图片加载框架,提供的使用方法和接口对开发者来说非常友好。其内部实现多是采用 block 的方式来实现回调,代码阅读起来可能没有那么直观, 但是使用起来非常便捷。 481 | 我写这篇文章主要是结合`SDWebImage`的源码给大家讲解 `SDWebImage` 加载图片的大概流程,里面细节很多, 我解读的只是其中的大流程, 具体细节可结合文章然后下载我带备注的源码好好阅读,希望对大家有所帮助。文章中有不对的地方,可以给我评论,我会在第一时间改正, 如果这篇文章对你所有帮助, 可以 `star` 下哈。 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | --------------------------------------------------------------------------------