2018.9.24

A, use,

1. Use the UIImageView + WebCache

[cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder.png"]].Copy the code

If you cancel the request before the image has finished loading, you will not receive a success or failure callback

	[cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
	                  placeholderImage:[UIImage imageNamed:@"placeholder.png"]
	                         completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
	                                ... completion code here ...
	                             }];
Copy the code

2. Use Manager, Downloader, and Cache separately

Use SDWebImageManager alone

	SDWebImageManager *manager = [SDWebImageManager sharedManager];
	[manager loadImageWithURL:imageURL
	                  options:0
	                 progress:^(NSInteger receivedSize, NSInteger expectedSize) {
	                        // progression tracking code
	                 }
	                 completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
	                    if (image) {
	                        // do something with image}}];Copy the code

Download images asynchronously using SDWebImageDownloader alone

	SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader];
	[downloader downloadImageWithURL:imageURL
	                         options:0
	                        progress:^(NSInteger receivedSize, NSInteger expectedSize) {
	                            // progression tracking code
	                        }
	                       completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
	                            if (image && finished) {
	                                // do something with image}}];Copy the code

SDImageCache supports memory caching and asynchronous disk caching (optional). You can use singletons or create an instance of SDImageCache with its own namespace. To add a cache:

	[[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey];
Copy the code

By default, image data is cached in both memory and disk. If you want to use memory cache only, you can use the following methods:

	[[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey toDisk:NO];
Copy the code

While reading the cache can use queryDiskCacheForKey: done: method, the key is the only image cache, usually is the picture of the absolute URL.

	SDImageCache *imageCache = [[SDImageCache alloc] initWithNamespace:@"myNamespace"];
	[imageCache queryDiskCacheForKey:myCacheKey done:^(UIImage *image) {
	    // image is not nil if image was found
	}];
Copy the code

Second, the structure of

1. The module

  • Download (SDWebImageDownloader)
  • Cache (SDImageCache)
  • Combine caching and downloading capabilities (SDWebImageManager)
  • UIImageView/UIButton (UIImageView+WebCache etc.)

MKAnnotationView: The map pin is a class in the MapKit framework that inherits from UIView and is used to display the annotation information on the map. It has an image property that is used to set the image. Use of MKMapView official document

2. Directory structure

3. Core logic

Process diagram source: J_Knight_ : SDWebImage source code analysis, explain clearly, thank you very much.

In a UIImageView call: sd_setImageWithURL: placeholderImage: options: progress: completed:

  • Cancel the current loading task operation
  • Set the placeholder map
  • If the URL is notnilthroughManagerSingleton to enable image loading operation

In downloadImageWithURL: options: progress: completed: in the first to take pictures of the Cache key (default is image URL) to Cache the singleton reads the memory Cache, if you have, just returned to the Manager; If not, the asynchronous thread is started to read the disk cache with the MD5 processed key. If the disk cache is found, it is synchronized to the memory cache and then returned to the Manager. DownloadImageWithURL returns a SDWebImageCombinedOperation object, this object contains a cacheOperation and a cancelBlock.

If there are no images in memory or disk cache, Manager will call the Downloader singleton -downloadiMageWithURL: options: Progress: completed: Method, save the incoming progressBlock and completedBlock, and the first time you download the image for that URL, Create a NSMutableURLRequest object and a SDWebImageDownloaderOperation object, And add the SDWebImageDownloaderOperation object to the downloadQueue asynchronous download task to start.

DownloaderOperation wraps a network request with an NSURLConnection, and uses a runloop to keep the NSURLConnection alive until a response is received. When downloading images, Monitored NSURLConnection callback – the connection: didReceiveData: method will be responsible for the processing and callback, progress – the connectionDidFinishLoading: Method is responsible for converting data to image, decoding the image, and finally calling completedBlock.

When the image download request in DownloaderOperation is complete, it is called back to the Downloader, which in turn calls back to the Manager, who caches the image to memory and disk (optional), and calls back to the UIImageView. UIImageView, go back to the main thread and set the image property.

4. Call the sequence diagram

Third, implementation strategy

1. Cache policy

SDImageCache manages a memory cache and (optionally) a disk cache, and writes to the disk cache asynchronously without blocking the main thread.

Why is caching needed?

  • Space for time, faster
  • Reduce unnecessary network requests and save traffic

1.1 Memory Cache

Memory caching is implemented through a class for AutoPurgeCache that inherits from NSCache.

NSCache (official NSCache document) An NSCache is a key-value container similar to an NSMutableDictionary.

  • Automatic deletion mechanism: When the system memory is tight,NSCacheSome cache objects are automatically deleted
  • Thread safety: From different threads to the sameNSCacheYou do not need to add, delete, modify, or check an object
  • Different from theNSMutableDictionary.NSCacheThe key is not copied when the object is stored

1.2 Disk Cache

Disk caching is implemented by asynchronous operation NSFileManager to store cached files to the sandbox.

1.3 Cache Operations

  1. Initialize the-initMethod is called by default-initWithNamespace:Method,-initWithNamespace:The method is called again-makeDiskCachePath:Method to initialize the cache directory path and also calls the-initWithNamespace:diskCacheDirectory:Method to implement initialization. Initialize the method call stack:
-init
    -initWithNamespace:
        -makeDiskCachePath: 
        -initWithNamespace:diskCacheDirectory:
Copy the code

-initWithNamespace:diskCacheDirectory: Initialize instance variables, properties, set property defaults, and set the full cache directory path for namespace. In addition, added notification observer for clearing the memory cache when memory is tight, as well as cleaning the disk cache when the program terminates and when the program goes back to the background.

  1. Write to the cachestoreImage:
- (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk{
}
Copy the code

Three parameters are written. When you add the memory cache, you calculate the pixels first, and then add them to the disk cache. When you add the disk cache, if you need to convert the image passed in to NSData before storage, instead of using the image passed in directly, you need to convert the image to the corresponding NSData object in a different image format.

NSData is used to wrap data. It stores binary data and shields the differences between data. Data such as text, audio and image can be stored with NSData.

Determine the image format: If imageData is nil, So if the image has an alpha channel and if the imageData is not nil, then the first 8 bits of imageData are going to be a PNG, because a PNG image has a unique signature, The first eight bits are in decimal: 137 80 78 71 13 10 26 10

Get imageData, with the help of NSFileManager image binary storage to the sandbox, stored file name is the string generated after the KEY MD5 processing. Default sandbox path: library-Caches

SandBox a security system that states that an application can only read files in a folder created for that application and cannot access content elsewhere. Save all non-code files such as images, sounds, property lists and text files. Permission authentication is required for applications to request or receive data. By default, each sandbox contains three folders: Documents, Library, and TMP

  • Documents: Saves the data generated when the application is running and needs to be persisted. ITunes backs up this directory.
  • Library: Default Settings or other state information for stored programs;
    • Caches: Stores data generated during the application runtime that needs to be persisted, usually large and unimportant data that does not need to be backed up. ITunes does not back up this directory, and the files in this directory are not deleted when the app exits.
    • Preferences: Preferences file, which iTunes backs up.
  • TMP: Saves the temporary data required by the application running, and deletes the corresponding files from the directory after use. The system may also clear files in this directory when the application is not running. ITunes does not back up this directory.
  1. Read cachequeryDiskCacheForKey
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock {
}
Copy the code

It’s going to return an NSOperation object and it’s going to read the memory cache first, and then the disk cache if it doesn’t. When reading the disk cache, it looks in the sandbox first, and if it doesn’t, it looks in the customPaths (that is, the bundle). Once found, convert the data. The following steps are the same as the image processing steps after the successful download – first convert the data to image, then scale it according to @2x and @3x in the file name, and then decompress it if needed.

  1. Cleaning the disk cache Each time an image is loaded, another image is added to the cache, so you need to periodically clear part of the cache.
  • Clean: Deletes some cache files
  • Clear: Deletes the entire cache directory

indicators

  • Cache validity period: PassedmaxCacheAgeProperty Settings, one week by default
  • Maximum cache size: YesmaxCacheSize, the default is 0.

SDImageCache added informed observer during initialization, so at the time of application will terminate and retreated to the background, will be called – cleanDiskWithCompletionBlock: methods for asynchronous cleaning the cache. Clean the disk cache: If maxCacheAge is set, delete the expired files, record the properties and total volume of the files, sort the files by change time from morning to night, and then iterate through the array of files, deleting them one by one. Until the total volume is less than desiredCacheSize, which is half of maxCacheSize.

2. Downloader download policy

The main task

  • Asynchronous download image management
  • Image loading optimization

In the concrete implementation: + initialize mainly by registered notification to download SDNetworkActivityIndicator to monitor events, to show and hide the status bar on the network activity indicator. In order to let SDNetworkActivityIndicator files don’t have to import the project (if not), the way the runtime is used here to realize dynamically create class and calls the method.

+ (void)initialize {
	if (NSClassFromString(@"SDNetworkActivityIndicator")) {
		id activityIndicator = [NSClassFromString(@"SDNetworkActivityIndicator") performSelector:NSSelectorFromString(@"sharedActivityIndicator")];
		# remove first SDNetworkActivityIndicator informed observer
		# add SDNetworkActivityIndicator informed observer}}Copy the code

The +sharedDownloader method calls the -init method to create a singleton. The -init method does some initialization and default Settings, including the maximum concurrency (6), the download timeout (15s), and so on.

Core method: -downloadiMageWithURL: Options: Progress: completed: method first by calling -addProgresscallback: andCompletedBlock: forURL: CreateCallback: method to save the callback block-addProgresscallback for each URL:… Method does an error check to see if the URL is empty, and then saves the URL’s progressBlock and completedBlock to the URLCallbacks property.

The URLCallbacks attribute is an NSMutableDictionary object, the key is the URL of the image, and the value is an array containing multiple groups of callback information for each image.

Because it is possible to download multiple images at the same time, it is possible for multiple threads to access the URLCallbacks property at the same time. To ensure thread safety, dispatch_barrier_sync is used to execute the tasks added to the barrierQueue in steps so that only one thread can operate on the URLCallbacks at a time.

- (void)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock andCompletedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock forURL:(NSURL *)url createCallback:(SDWebImageNoParamsBlock)createCallback { //1. Determine if the URL is nil, if it is nil, call back completedBlock, return the result of failure, and thenreturnThe url will be used as the key to store callbacks. Handle multiple download requests for the same URL (MARK: use dispatch_barrier_sync to ensure that only one thread is working on URLCallbacks at a time) : //3. Take the callBacksForURL from the property URLCallbacks(a dictionary) (this is an array, because there may be more than one place to download a URL) //4. If not, which means the URL was downloaded for the first time, then initialize a callBacksForURL and put it in the property URLCallbacks //5. Add a dictionary wrapped with Callbacks (ProgressBlocks and completedBlock) to the callBacksForURL array //6. Update the callBacksForUR} of the url stored by URLCallbacksCopy the code

If this URL is downloaded for the first time, then the createCallback is called, and the createCallback’s main job is to create and start the download task

CreateCallback method calls – [SDWebImageDownloaderOperation initWithRequest: options: Progress:] method to create SDWebImageDownloaderOperation download task.

3. The other

3.1 SDWebImageDecoder

Since the imageWithData function of UIImage decompresses Data into ARGB images every time the drawing is done, there will be a decompression operation every time the drawing is done, which only requires instantaneous memory, but the efficiency is very low. In order to improve efficiency, SDWebImageDecoder is used to draw the resources wrapped under Data on another picture, so that this new picture does not need to be decompressed repeatedly, which is the practice of space for time.

The decoding of the picture is actually the binary data of the picture into the process of pixel data, SD to redraw the picture, get a bitmap. RGBA color space is required to display images (what is RGBA? , but PNG and JPEG are not RGBA formats themselves. So create a BitmapImage, render the image in the non-UI thread first as a pre-decode, and then bring it to the UIImage to display. IOS Image decoding

3.2 SDWebImagePrefetcher

Pre-download is available, but the download is of low priority.

Fourth, the TIPS

Use NSOperation to manage operations

1. Features of NSOperation

  • stateStateOperation: isReady -> isExecuting -> isFinished

State is obtained implicitly through the KVO notification of the keypath, rather than explicitly through a state attribute. When an operation is ready to be executed, it sends a KVO notification to isReadykeyPath, and the corresponding property value is changed to YES.

To construct a consistent state, each property is mutually exclusive with the others: isReady: returns YES if the operation isReady to execute, and NO if the operation it depends on has some unfinished initialization step. IsExecuting: return YES if operation isExecuting its task, NO otherwise. IsFinished: YES is returned after the task has completed successfully or been canceled.

NSOperationQueue kicks only the operation whose isFinished is YES out of the queue. The operation whose isFinished is NO will never be removed. Therefore, ensure that the operation is correct during implementation to avoid deadlocks

  • cancelCancellationCancel an operation in two cases:
    • Explicitly call the cancel method
    • Operation Other dependent operations fail to be executed

The cancellation of NSOperation is also obtained by KVO of isCancelledkeypath. When a subclass of NSOperation overrides the cancel method, be careful to clear up internally allocated resources. Notice that ‘isCancelled’ and ‘isFinished’ have both changed to YES, and ‘isExecuting’ is NO.

Cancelled: with one “L” for method (verb) isCancelled: with two “L” for property (adjective)

  • The queuePriority property can be set to increase or decrease the Priority of an operation. The queuePriority property can be set to the following values: NSOperationQueuePriorityVeryHigh NSOperationQueuePriorityHigh NSOperationQueuePriorityNormal NSOperationQueuePriorityLow NSOperationQueuePriorityVeryLow in addition, the operation can specify a threadPriority value, its range is 0.0 to 1.0, 1.0 represents the highest priority. QueuePriority: determines the priority of the order of execution threadPriority: determines the amount of computing resources to be allocated after the operation starts executing

  • If you need to divide a large task into subtasks, use Dependencies to ensure that the task is executed in order. If operation B depends on operation A, operation B can be executed only when the isFinished value of operation A is YES. Avoid deadlock caused by cyclic dependency

[resizingOperation addDependency:networkingOperation];
[operationQueue addOperation:networkingOperation];
[operationQueue addOperation:resizingOperation];
Copy the code
  • CompletionBlock When an NSOperation completes, the completionBlock is executed exactly once. When a network request is completed, the returned data can be processed in the completionBlock.

Reference: mattt-nsoperation

2. How to use NSOperation in Manager

SDWebImageCombinedOperation when the url is correct, after the incoming instance will be a very strange “operation”, it is actually a follow SDWebImageOperation agreement NSObject subclass. And the agreement is very simple:

@protocol SDWebImageOperation <NSObject>
- (void)cancel;
@end
Copy the code

I’m just wrapping this SDWebImageOperation class up into a class that looks like NSOperation but it’s not an NSOperation, The only thing this class has in common with NSOperation is that it can respond to the cancel method. Please read it several times). This class actually exists to make the code more concise, because calling the cancel method of this class will cancel both operations it holds.

// SDWebImageCombinedOperation
// cancel # 1

- (void)cancel {
    self.cancelled = YES;
    if (self.cacheOperation) {
        [self.cacheOperation cancel];
        self.cacheOperation = nil;
    }
    if(self.cancelBlock) { self.cancelBlock(); _cancelBlock = nil; }}Copy the code

This class, on the other hand, is supposed to be designed for a more concise cancel operation.

3. How to use NSOperation in the Downloader

Each image download makes an asynchronous HTTP request, managed by DownloaderOperation.

NSOperation DownloaderOperation inheritance, observe SDWebImageOperation, NSURLConnectionDataDelegate agreements.

The SDWebImageOperation protocol defines only one method – Cancel, which is used to cancel the operation.

When a created DownloaderOperation object is added to the downloadQueue of the downloader, the -start method on the object is automatically called. The -start method first creates a NSURLConnection to download the image data, then opens the connection and issues the connection to start the image download. When all the image data has been downloaded, the completionBlock passed by the Downloader is called. The image download is complete.

So the image data download is done by an NSConnection object whose entire life cycle (from creation to download) is controlled by the DownloaderOperation. Adding operation to the operation Queue allows multiple images to be downloaded simultaneously

Other small TIPS

Use the NS_OPTIONS bit operation enumeration type. At the same time, you can use the and operator to determine whether an enumeration option is set. Only one bit of each enumeration option is 1 and the rest bits are 0. So only the other binary value involved in the operation is also 1 in the same position, With the result of the operation won’t for 0. Eg. 0101 (equivalent to SDWebImageDownloaderLowPriority | SDWebImageDownloaderUseNSURLCache) & 0100 (= 1 < < 2, Namely SDWebImageDownloaderUseNSURLCache) = 0100 (> 0, which means that the option parameter set SDWebImageDownloaderUseNSURLCache)

Initialization Generally speaking, a management class has a global singleton object, and the initialization method is designed according to the business requirements. When designing a class, you should tell other developers how the class should be created through proper initialization methods.

  • (nonnull instancetype) sharedImageCache singleton
  • (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns initializes the namespace
  • (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns diskCacheDirectory:(nonnull NSString *)directory NS_DESIGNATED_INITIALIZER Specifies the namespace and path.

Use @synchronized: Manager uses @synchronized for both failedURLs and runningOperations, replaced with GCD implementation in the new version

Download a high-resolution image that causes a memory explosion

Five, the reflection

1. With the latest version (V4.4.2)

Function extension

  • useFLAnimatedImageTo process the GIFs
  • increasedSDImageCacheConfigAfter configuring the cache, you can choose whether to decompress the cache, iCloud, and the maximum cache size.
  • Large image scaling logic:sd_decompressedAndScaledDownImageWithImage:To avoid scaling the Image too large, the method is to divide the Image into a series of small size squares, and then each square to get the Image and draw to the target BitmapContext

2. Optimize the cache

How to optimize the cache?

  • Add and delete search speed
  • Improve hit rate

(1). -> LRU -> LRU+FIFO

(2). Cache blur matching requests of different sizes for the same image. If there is a larger image in the cache, it is considered a cache hit.