1. Internal implementation of SDWebImage

1.1 Implementation Procedure

1. The entrancesetImageWithUrl: placeHolderImage: options: take placeHolderImage display, then SDWebImageManager according to URL starts processing images. 2. Enter SDWebImageManager - downloadWithURL: delegate: options: the userInfo: to SDImageCache download cache lookup images are from queryDiskCacheForKey: delegate: u serInfo: 3. SDImageCacheDelegate imageCache:didFineImage: imageCache:didFineImage:forKey: the userInfo: to SDWebImageManager. 4. SDWebImageManagerDelegate callback webImageManager: didFinishWithImage: the UIImageView + The front end displays images such as WebCache. 5. If not, generate NSInvocationOperation to add to the queue to start looking from disk to see if the image has been cached 6. Try to read the image file in the disk cache directory according to the URLKey. This step is performed in NSOperation, so the result callback notifyDelegate is performed back on the main thread. 7. If the last operation to read from the hard disk images, add images to the memory cache (if the free memory is too small Will first clear the memory cache). SDImageCacheDelegate callback imageCache: didFinishImage:forKey: the userInfo: then the callback display pictures. 8. If you do not read from the hard disk cache directory images, point out the picture does not exist in all cache, need to download the pictures, the callback imageCache: didNotFindImageForKey: the userInfo. 9. Share or rebuild a downloader SDWebImageDownLoader to start downloading images 10. Images are downloaded is done by the NSURLConnection, implement relevant delegate to judge images are downloaded, the download is complete and failed download 11. The connection: didReceiveData: use ImageIO 12 made according to the pictures download progress load effect. The connectionDidFinishLoading: data download to SDWebImageDecoder do image decoding processing after the completion of 13. Image decoding is done in an NSOperationQueue without slowing down the main thread UI. If there is a need for secondary processing of the downloaded images, it is best to do it here, it will be much more efficient. In the main thread notifyDelegateOnMainThreadWithInfo: declaration of decoding complete imageDecoder: didFinishDecodingImage: the userInfo: callback to SDWebImageDownloader 15. ImageDownLoader: didFinishWithImage: callback told SDWebImageManager complete 16 images are downloaded. Notify all downloadDelegates that the download is complete, call back to show image 17 where needed. Save the image to SDImageCache in memory cache and hard disk cache at the same time, write file to hard disk is also completed with a separate NSInvocationOperation, avoid slowing down the main thread 18. SDImageCache registers some message notifications when initializing, clears memory image cache when memory warning or retreating to the background, and clears expired images when the application ends 19. SDWI also provides UIButton + WebCache and MKAnnptation + 20. SDWebImagePrefetcher can download images in advance to facilitate subsequent useCopy the code

Here’s another illustration:

1.2 API parameter enumeration types

1.2.1 SDWebImageOptions: Image download policy

For example, SD provides UIImageView+WebCache.m classification for UIImageView, which has these apis:

- (void)sd_setImageWithURL:(NSURL *)url {
    [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil];
}

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder {
    [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil];
}

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options {
    [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil];
}

- (void)sd_setImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock {
    [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:completedBlock];
}

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock {
    [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:completedBlock];
}

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock {
    [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:completedBlock];
}
Copy the code

All of these apis use SDWebImageOptions, either directly or indirectly. What types of parameters do you remember? What does each do?

By viewing the sdWebImagemanager. h source code, we can know as follows:

Typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {// By default, the URL will be blacklisted when the URL fails to download, so the library will not try again. SDWebImageRetryFailed = 1 << 0, // By default, image download starts at UI interaction, this tag disables this feature, This download delays to the UIScrollView deceleration SDWebImageLowPriority = 1 < < 1, / / the marker to disable the disk cache SDWebImageCacheMemoryOnly = 1 < < 2, / / the tag enable progressive download, The image is displayed gradually during the download process, just like the browser. / / by default, the image after the download is complete one-time show SDWebImageProgressiveDownload = 1 < < 3, / / even if the image cache, also expect the HTTP response cache control, and in case of need to refresh from the remote images. // Disk cache will be handled by NSURLCache instead of SDWebImage, as SDWebImage can cause minor performance downloads. // This tag helps handle images that change after the same request URL. If the cached image is refreshed, the completion block is called once with the cached image and then SDWebImageRefreshCached = 1 << 4 with the final image. In iOS 4+, the download continues when the application is in the background. This will require the system to give the request extra time to complete // if the background task times out, The operation was cancelled SDWebImageContinueInBackground = 1 < < 5, / / by setting NSMutableURLRequest HTTPShouldHandleCookies = YES; SDWebImageHandleCookies = 1 << 6 to handle cookies stored in NSHTTPCookieStore / / to allow untrusted SSL certification SDWebImageAllowInvalidSSLCertificates = 1 < < 7, / / by default, the images are downloaded according to the order of the team to perform. SDWebImageHighPriority = 1 << 8, // By default, the placeholder image is loaded at the same time as the image is loaded. The tag delay placeholder images load until the image has been loaded with SDWebImageDelayPlaceholder = 1 < < 9, / / we don't usually call animation image transformDownloadedImage agent method, Because most of the transformation code can manage it. // Use this box office and do not convert in any case. SDWebImageTransformAnimatedImage = 1 << 10, };Copy the code
1.2.2 SDImageCacheType: Image cache policy

For example, two examples of setting pictures

[self.image2 sd_setImageWithURL:imagePath2 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
        NSLog(@"Here's something to do after the image is loaded.");
}];
Copy the code
/ / use the default image, and use the block in doing something [after the completion of the self. The image1 sd_setImageWithURL: imagePath1 placeholderImage: [UIImage imageNamed: @"default"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
        NSLog(@"What to do after the image is loaded.");
}];
Copy the code

There are also examples of getting download progress

/ / use the default image, and use the block in doing something [after the completion of the self. The image1 sd_setImageWithURL: imagePath1 placeholderImage: [UIImage imageNamed: @"default"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
        NSLog(@"What to do after the image is loaded."); 
}];
Copy the code

All of the examples above have a parameter called SDImageCacheTyp. What do you remember about this parameter?

  • SDImageCacheType
// Define the Cache type typedef NS_ENUM(NSInteger, SDImageCacheType) {// Do not use Cache to obtain the image, still download the image from the Web SDImageCacheTypeNone, SDImageCacheTypeDisk; SDImageCacheTypeMemory; SDImageCacheTypeMemory;Copy the code

2. Set the maximum cache and time

  • SDImageCache class source
@property (Assign, nonatomic) BOOL shouldDecompressImages; @property (assign, nonatomic) NSUInteger maxMemoryCost; @property (assign, nonatomic) NSInteger maxCacheAge; @property (assign, nonatomic) NSUInteger maxCacheSize;Copy the code
  • Example of setting maxCacheSize
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager.imageCache setMaxMemoryCost:1000000]; // Set the total cache size, which defaults to 0 without limit [Manager.imagecache]setMaxCacheSize:640000]; // Set the limit size of a single image [Manager.imageDownloadersetMaxConcurrentDownloads:1]; [Manager downloadImageWithURL:[NSURL URLWithString:@] [Manager downloadImageWithURL:[NSURL URLWithString:@"http://p9.qhimg.com/t01eb74a44c2eb43193.jpg"]
                      options:SDWebImageProgressiveDownload progress:^(NSInteger receivedSize, NSInteger expectedSize) {
                          NSLog(@"%lu", receivedSize);
                      } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
                          self.imageView1.image = image;
                          
                      }];
[manager downloadImageWithURL:[NSURL URLWithString:@"http://img.article.pchome.net/00/28/33/87/pic_lib/wm/kuanpin12.jpg"]
                      options:SDWebImageProgressiveDownload progress:^(NSInteger receivedSize, NSInteger expectedSize) {
                          NSLog(@"%lu", receivedSize);
                      } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
                          self.imageView2.image = image;
                          
                      }];
NSUInteger size = [manager.imageCache getSize];
NSUInteger count = [manager.imageCache getDiskCount];
NSLog(@"size = %lu", size); // 644621 (two test images) NSLog(@"count = %lu", count); // 2
[manager.imageCache clearDisk];
size = [manager.imageCache getSize];
count = [manager.imageCache getDiskCount];
NSLog(@"sizeClean = %lu", size);  //  0
NSLog(@"countClean = %lu", count); // 0 uses clearCopy the code

3. Distinction: Three kinds of caches (memory image cache, disk image cache, memory operation cache)

  • First view the memory image cache, memory image cache is not, after the generation operation, view the disk image cache
  • If the disk image cache is available, the image is loaded into the memory cache. If not, the image is downloaded
  • Determine whether the download operation exists before establishing it
  • By default, downloaded image data is cached both in memory and on disk

About cache locations

  • Memory caching is implemented via NSCache subclass AutoPurgeCache;
  • Disk cache is done by NSFileManager file storage (the default path for/Library/Caches/default/com hackemist. SDWebImageCache. The default), asynchronous implementation.

About the image download operation

Most of the work of SDWebImage is done by the cache object SDImageCache and the asynchronous downloader management object SDWebImageManager.

SDWebImage image download by SDWebImageDownloader this class to achieve, it is an asynchronous download manager, the download process to add the image loading has been optimized. The actual image download is a custom Operation, which is added to the download manager’s Operation queue downloadQueue. The Operation depends on the NSURLConnection class provided by the system to download the image.

4. Hd and LOW definition pictures and network environment problems

  • Network Judgment issues – Using AFNetworking’s API first, enable monitoring
/ / AppDelegate. M file - (BOOL) application: (UIApplication *) application didFinishLaunchingWithOptions: (NSDictionary *) launchOptions {/ / monitor network status [[AFNetworkReachabilityManager sharedManager] startMonitoring]; }Copy the code

Monitoring management is then obtained where needed

/ / the following code is used in the need to monitor the status of the network AFNetworkReachabilityManager * MGR = [AFNetworkReachabilityManager sharedManager];if(Mgr. isReachableViaWiFi) {// Download the original image while using Wifi;else{// Others, download the miniatures}Copy the code
  • It’s not as simple as downloading the hd image for WIFI and the thumbnail for cellular. Factors to consider and utilize caching.

  • A typical example

- setItem:(CustomItem *)item { _item = item; UIImage *placeholder = [UIImage imageNamed:@]"placeholderImage"]; // Get the original image from the memory sandbox cache,  UIImage *originalImage = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:item.originalImage];if(originalImage) {self.imageView.image = originalImage; self.imageView.image = originalImage; }else{/ / cache memory \ box without the original image AFNetworkReachabilityManager * MGR = [AFNetworkReachabilityManager sharedManager];if(Mgr. isReachableViaWiFi) {// Wifi is in use, Download the original [self imageView sd_setImageWithURL: [NSURL URLWithString: item. The originalImage] placeholderImage: placeholder]; }else if(mgr.isReachableViaWWAN) {// when using the built-in network of mobile phones // it is assumed that the user's configuration items are stored in the sandbox by using NSUserDefaults // [[NSUserDefaults standardUserDefaults]setBool:NO forKey:@"alwaysDownloadOriginalImage"];
            //    [[NSUserDefaults standardUserDefaults] synchronize];
# Warning Reading the user's configuration items from the sandbox: will the original image still be downloaded in a 3G / 4G environment
            BOOL alwaysDownloadOriginalImage = [[NSUserDefaults standardUserDefaults] boolForKey:@"alwaysDownloadOriginalImage"];
            if(alwaysDownloadOriginalImage) {/ / download artwork [self. ImageView sd_setImageWithURL: [NSURL URLWithString: item. The originalImage] placeholderImage:placeholder]; }else{/ / download insets [self imageView sd_setImageWithURL: [NSURL URLWithString: item. ThumbnailImage] placeholderImage: placeholder]; }}else{/ / no Internet UIImage * thumbnailImage = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey: item. ThumbnailImage];if(thumbnailImage) {self.imageview.image = thumbnailImage; }else{self.imageview.image = placeholder;}} {self.imageview.image = placeholder; }}}}Copy the code

5. Possible problems

5.1 APP memory collapses due to high definition large images not processed in the background?

  • Train of thought, rewrite sd_imageWithData method source code, can refer to https://blog.csdn.net/benyoulai5/article/details/50462586

5.2 How can I continue to download UIImageView in the Cell after the Cell is removed from the screen? Remove the current binding of UIImageView.

  • The API that SD provides for setting up the UIImageView is essentially called the following API:
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock {
Copy the code
  • In its method implementation body, the first sentence is[self sd_cancelCurrentImageLoad];
  • This is the key, when the cell in the TableView contains a UIImageView that is being reused, this line of code is called first to ensure that the ImageView download and cache combination is cancelled. If: (1) The last assigned image is being downloaded, the download will not proceed; Wself. image = image), so the same image will not be displayed as the re-used cell. (3) The above two situations only occur when the Internet speed is very slow and the processing speed of the phone is very slow. In fact, the probability of occurrence is very small. This time the cell is a reuse cell, and the image of the imageView is kept. In this case, the SD will set the placeholder statement in the implementation method, and the image will be temporarily set to the placeholder. If the placeholder is empty, it means that the image will be temporarily emptied.

  • The internal call stack for removing bindings in SD is:
- (void)sd_cancelCurrentImageLoad {
    [self sd_cancelImageLoadOperationWithKey:@"UIImageViewImageLoad"];
}
Copy the code
- (void)sd_cancelImageLoadOperationWithKey:(NSString *)key {
    // Cancel in progress downloader from queue
    NSMutableDictionary *operationDictionary = [self operationDictionary];
    id operations = [operationDictionary objectForKey:key];
    if (operations) {
        if ([operations isKindOfClass:[NSArray class]]) {
            for (id <SDWebImageOperation> operation in operations) {
                if(operation) { [operation cancel]; }}}else if([operations conformsToProtocol:@protocol(SDWebImageOperation)]){ [(id<SDWebImageOperation>) operations cancel]; } [operationDictionary removeObjectForKey:key]; }}Copy the code

5.3 How can SDWebImage achieve the task of pausing the data source when the screen is sliding? Through NSOperationQueuesetSuspend?

3.1 SDWebImage based on NSURLConnection

Old versions of SDWebImage based on NSURLConnection (at least as of July 2014) are made by: NSURLConnection works on the main thread. Even though NSURLConnection works on the child thread, because the UI-related operations and the setImage in the callback are all on the same main thread, Sliding the screen causes the main thread’s Runloop to switch mode to UITrackingRunLoopMode, at which point the original kCFRunLoopDefaultMode source (in this case, NSURLConnectionsetImage) is paused. Runloop cannot perform a callback.

It is designed to prevent network-related operations from being blocked on the main thread. Correct: Network-related operations are on the subthread. Mode switching on the main thread does not affect the subthread, but it does have the effect of suspending the data download task while the screen is sliding. Swiping doesn’t pause the data download, it pauses the setImage on the same main thread.

In the old version, the SDWebImageDownloaderOperation. M file to have such a words:

3.2 SDWebImage based on NSURLSession

However, the new version of SDWebImage is based on NSURLSession. The main difference between this NSURLSession and NSURLConnection is that it is based on the runloop control of the main thread subthread. Instead, the subthread is opened by NSOperation, so agreeing to the main thread’s runloop mode switch does not affect the subthread’s operation. So, the new version of SDWebImage doesn’t have this “swip-and-pause” effect. Fix: Again, swiping does not pause data download, it pauses setImage on the same main thread.

If necessary, there are two ways to rewrite the setImage method and set the working mode in it, just like the old version of SDWebImage. One is to change the thread or mode of setImage. Another option is to listen for the pull state of a ScrollView. When the ScrollView’s agent method hears that it’s being pulled, suspend the operation.