In the previous article, we have learned about the implementation mechanism and elimination strategy of NSCache. Since the article is too long, it is not conducive to reading. So in this article, we will explore and explain the cache processing mechanism in NSURLCache and SDWebImage.
The table of contents I will continue the previous article, in order to compare the reading.
2. NSURLCache
2.1 introduction
First send the official document to NSURLCache
First of all, we all know that when using NSURLCache to cache request data, there is also a default cache processing. So what do we need to do? What does the native default do?
The
NSURLCache
class implements the caching of responses to URL load requests by mappingNSURLRequest
objects toNSCachedURLResponse
objects. It provides a composite in-memory and on-disk cache, and lets you manipulate the sizes of both the in-memory and on-disk portions. You can also control the path where cache data is stored persistently.
What does that mean? The important thing is that it provides disk cache and memory cache, and lets the user specify the size of the disk and memory cache. It is not important to know when the contents of the cache on disk or in memory will be known.
And it allows us to focus on different strategies to meet flexible and changing needs.
2.1.1 Cache Policy
When using NSURLSession, we can specify NSURLRequestCachePolicy directly via NSMutableURLRequest.
typedef NS_ENUM(NSUInteger.NSURLRequestCachePolicy)
{
NSURLRequestUseProtocolCachePolicy = 0.// Default policy
NSURLRequestReloadIgnoringLocalCacheData = 1.// Ignore cache, must download from remote address;
NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4.// Unimplemented not implemented
NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData.NSURLRequestReturnCacheDataElseLoad = 2.// Whether the cache expires or not, use the cache if it does, and request data if it does not.
NSURLRequestReturnCacheDataDontLoad = 3.// Whether the cache expires or not, use the cache if it does, and fail if it does not.
NSURLRequestReloadRevalidatingCacheData = 5.// Unimplemented not implemented
};
Copy the code
Before we get to NSURLCache, let’s take a look at the default HTTP caching mechanism. This is the default policy for NSURLRequestCachePolicy.
2.1.2 HTTP Cache Policy
Let’s start with an example that uses the HTTP caching policy by default.
- (void)example_1{
NSURL *url = [NSURL URLWithString:@"http://via.placeholder.com/50x50.jpg"];
// The HTTP cache policy is used by default
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(@"error warning : %@",error);
}else{
// Read data from cache!
NSData *tempData = data;
NSString *responseStr = [[NSString alloc] initWithData:tempData encoding:NSUTF8StringEncoding];
NSLog(@"response:%@",response);
}
}] resume];
}
Copy the code
Print result:
response:<NSHTTPURLResponse: 0x600002ff7380> { URL: http://via.placeholder.com/50x50.jpg } { Status Code: 200, Headers {
"Accept-Ranges" = (
bytes
);
"Cache-Control" = (
"max-age=604800"
);
Connection = (
"keep-alive"
);
"Content-Length"= (807);"Content-Type" = (
"image/jpeg"
);
Date = (
"Wed, 18 Sep 2019 06:32:38 GMT"
);
Etag = (
"\"5d5c8aae-327\""
);
Expires = (
"Wed, 25 Sep 2019 06:32:38 GMT"
);
"Last-Modified" = (
"Wed, 21 Aug 2019 00:05:02 GMT"
);
Server = (
"Nginx / 1.6.2"
);
"X-Cache"= ( L1 ); }}Copy the code
Pragma and cache-Control are two fields that Control the Cache switch in HTTP. Pragma is an old artifact that has been gradually abandoned, and some sites still use these two fields for backward compatibility. I won’t introduce it here.
Cache-Control
When cache-control is used in a request, its optional values are:
Cache invalidation
In caching, we need a mechanism to verify that the cache is valid. For example, when the server resources are updated, the client needs to refresh the cache in time. Alternatively, the resource on the client expires, but the resource on the server is still old. In this case, resending the resource does not need to be done. Cache validation is used to solve these problems,
In HTTP 1.1, we focused on last-Modified and eTAG fields.
Last-Modified
When a server returns a resource, it returns the Last Modified time of the resource to the client in the last-Modified field. If-modified-since or if-unmodified-since with last-modified is the next request from the client, and the server checks whether the time is consistent with the Last Modified time of the server:
- If they are the same, the 304 status code is returned, and the resource is not returned.
- If not, return 200 and the modified resource with the new time.
The difference between if-modified-since and if-unmodified-since is:
If-modified-since: tells the server to return status code 304 If the time is consistent
If-unmodified-since: tells the server to return status code 412 If the time is inconsistent
Etag
Judging by the modification time alone is still flawed, for example, the last modification time of the file has changed, but the content has not changed. For such cases, we can use Etag.
Etag mechanism:
The server calculates the resource using an algorithm and obtains a string of values (similar to the hash value of a file). The server then returns the value to the client using Etag. The client uses if-none-match or if-match to add the value to the next request. If they are, do not return the resource.
If-none-match differs from if-match:
If-None-Match
: tells the server to return the status code if it is consistent304
If not, resources are returnedIf-Match
: tells the server to return a status code if it is inconsistent412
How can last-Modified produce Etag?
You might think that last-Modified is enough to let the client know if the local cached copy is new enough, so why Etag? Etag was introduced in HTTP 1.1 to address several last-Modified issues:
-
The last-Modified tag is accurate only to the second level. If a file has been Modified more than once in a second, it will not accurately mark the modification time of the file
-
If some files are generated regularly, sometimes the contents are unchanged but last-Modified changes, making the file uncacheable
-
The server may not obtain the correct file modification time or the time on the proxy server may be inconsistent with that on the proxy server
Etag is the unique identifier of the corresponding resource on the server side that is automatically generated by the server or generated by the developer, which can more accurately control the cache. Last-modified and ETag can be used together. The server will verify the ETag first. If the ETag is consistent, the server will continue to compare last-Modified, and finally decide whether to return 304.
Summary of HTTP caching mechanisms
The cache switch is pragma, cache-control.
The cache validations are: Expires, Last-Modified, and ETAG.
From the perspective of the process, they look like this: (Image from the Web: Browser loading HTTP cache mechanism)
- First request:
- Request again:
2.1.3 Viewing HTTP Cache Content
Add a breakpoint to get the sandbox, and let’s open the folder and see:
- Access path
- View sandbox files
- Use the DB view tool, which I’m using here
DB Browser for SQLite
- Select the first table
cfurl_cache_blob_data
Browse the data.
- Choose number one
response_object
Export binary on the rightbin
File using terminal directcat
Command to view the file.
As you can see, the HTTP request information is stored in this database table.
If you look further, you will see that all the information is stored in the various tables.
2.1.4 Using Other Cache Policies
Again, in the previous case, let’s modify it slightly:
- (void)example_1{
NSURL *url = [NSURL URLWithString:@"http://via.placeholder.com/50x50.jpg"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:15.0];
// The HTTP cache policy is used by default
// NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; \
// Compare services to check whether resources are updated
if (self.lastModified) {
[request setValue:self.lastModified forHTTPHeaderField:@"If-Modified-Since"];
}
// if (self.etag) {
// [request setValue:self.etag forHTTPHeaderField:@"If-None-Match"];
/ /}
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(@"error warning : %@",error);
}else{
// Read data from cache!
NSData *tempData = data;
NSString *responseStr = [[NSString alloc] initWithData:tempData encoding:NSUTF8StringEncoding];
self.lastModified = [(NSHTTPURLResponse *)response allHeaderFields][@"Last-Modified"];
// self.etag = [(NSHTTPURLResponse *)response allHeaderFields][@"Etag"];
NSLog(@"response:%@",response);
}
}] resume];
}
- (IBAction)reloadDataAction:(id)sender {
[self example_1];
}
Copy the code
Run, load for the first time, return 200, hit Reload to load again. Print the following:
lastModified
etag
3. NSURLCache and its own cache mechanism in SDWebImage
Open the SDWebImage source code directly. Go to the following method of sdWebImagedownloader. m.
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
options:(SDWebImageDownloaderOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
__weak SDWebImageDownloader *wself = self;
return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{
__strong __typeof (wself) sself = wself;
NSTimeInterval timeoutInterval = sself.downloadTimeout;
if (timeoutInterval == 0.0) {
timeoutInterval = 15.0;
}
// In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise
NSURLRequestCachePolicy cachePolicy = options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData;
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url
cachePolicy:cachePolicy
timeoutInterval:timeoutInterval];
/*... * /
}
Copy the code
It contains this remarkable passage:
NSURLRequestCachePolicy cachePolicy = options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData;
That is, SD will ignore NSURLCache by default unless options is specified as UseNSURLCache.
The purpose of this is to prevent multiple cache, avoid SD implementation of the cache and NSURLCache multiple cache caused by resource waste.
Similarly, in SDWebImageDownloaderOperation. J m, NSURLSessionDataDelegate willCacheResponse agent method Can also see:
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
willCacheResponse:(NSCachedURLResponse *)proposedResponse
completionHandler:(void(^) (NSCachedURLResponse *cachedResponse))completionHandler {
NSCachedURLResponse *cachedResponse = proposedResponse;
if (self.request.cachePolicy == NSURLRequestReloadIgnoringLocalCacheData) {
// Prevents caching of responses
cachedResponse = nil;
}
if(completionHandler) { completionHandler(cachedResponse); }}Copy the code
When set NSURLRequestReloadIgnoringLocalCacheData strategies, ignore NSURLCache cache.
3.1 Download option in SDWebImage
typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {
SDWebImageDownloaderLowPriority = 1 << 0,
SDWebImageDownloaderProgressiveDownload = 1 << 1,
SDWebImageDownloaderUseNSURLCache = 1 << 2,
SDWebImageDownloaderIgnoreCachedResponse = 1 << 3,
SDWebImageDownloaderContinueInBackground = 1 << 4,
SDWebImageDownloaderHandleCookies = 1 << 5,
SDWebImageDownloaderAllowInvalidSSLCertificates = 1 << 6,
SDWebImageDownloaderHighPriority = 1 << 7,
SDWebImageDownloaderScaleDownLargeImages = 1 << 8};Copy the code
That we also to search SDWebImageDownloaderIgnoreCachedResponse the options, SD did what processing.
- First search result:
SDWebImageDownloaderOperation.m
的start
The method includes this passage:
if (self.options & SDWebImageDownloaderIgnoreCachedResponse) {
// Grab the cached data for later check
NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:self.request];
if (cachedResponse) {
self.cachedData = cachedResponse.data; }}Copy the code
Load the disk cache data of NSURLCache for the following purposes.
- Second search result:
SDWebImageDownloaderOperation.m
的didCompleteWithError
There is a paragraph in the proxy method:
if (self.options & SDWebImageDownloaderIgnoreCachedResponse && [self.cachedData isEqualToData:imageData]) {
// call completion block with nil
[self callCompletionBlocksWithImage:nil imageData:nil error:nil finished:YES];
}
Copy the code
Here directly returned to nil, i.e. SDWebImageDownloaderIgnoreCachedResponse mechanism of the options is when the image is from NSURLCache access to, it returns nil.
So that’s NSURLCache, SDWebImage and HTTP cache strategy analysis. If there are any mistakes, please correct them.