iOS架构师_SDWebImage源码分析

2023-11-03

1.对SDdemo的分析

针对SD的3.8.2版本进行分析,因为从git上面直接下载SD的源码,项目运行会报错,缺少libwebp这个库,我们下载3.8.2版本,运行

这里写图片描述

libwebp 是个webp的图片格式,属于谷歌的库,所以只能翻墙去国外的网站上去找,文章最后会给出libwebp的文件,webp格式的会压缩图片,使文件变小,节约用户流量

缺失文件路径:/Users/mac/Desktop/项目名称/Vendors/libwebp

UML结构图:

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

时序图

这里写图片描述
这里写图片描述

系统运行顺序
这里写图片描述

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    //Add a custom read-only cache path
    NSString *bundledPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"CustomPathImages"];
    [[SDImageCache sharedImageCache] addReadOnlyCachePath:bundledPath];

    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    // Override point for customization after application launch.
    MasterViewController *masterViewController = [[MasterViewController alloc] initWithNibName:@"MasterViewController" bundle:nil];
    self.navigationController = [[UINavigationController alloc] initWithRootViewController:masterViewController];
    self.window.rootViewController = self.navigationController;
    [self.window makeKeyAndVisible];
    return YES;
}

主控制器MasterViewController

#import "MasterViewController.h"
#import <SDWebImage/UIImageView+WebCache.h>
#import "DetailViewController.h"

@interface MasterViewController () {
    NSMutableArray *_objects;
}
@end

@implementation MasterViewController

//Xcode4.5就可以省略了
@synthesize detailViewController = _detailViewController;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self)
    {
        self.title = @"SDWebImage";
        self.navigationItem.rightBarButtonItem = [UIBarButtonItem.alloc initWithTitle:@"Clear Cache"
                                                                                style:UIBarButtonItemStylePlain
                                                                               target:self
                                                                               action:@selector(flushCache)];

        // HTTP NTLM auth example
        // Add your NTLM image url to the array below and replace the credentials
        [SDWebImageManager sharedManager].imageDownloader.username = @"httpwatch";
        [SDWebImageManager sharedManager].imageDownloader.password = @"httpwatch01";

        _objects = [NSMutableArray arrayWithObjects:
                    @"http://www.httpwatch.com/httpgallery/authentication/authenticatedimage/default.aspx?0.35786508303135633",     // requires HTTP auth, used to demo the NTLM auth
                    @"http://assets.sbnation.com/assets/2512203/dogflops.gif",
                    @"http://www.ioncannon.net/wp-content/uploads/2011/06/test2.webp",
                    @"http://www.ioncannon.net/wp-content/uploads/2011/06/test9.webp",
                    nil];

        for (int i=0; i<100; i++) {
            [_objects addObject:[NSString stringWithFormat:@"https://s3.amazonaws.com/fast-image-cache/demo-images/FICDDemoImage%03d.jpg", i]];
        }//追加了100条数据

    }
    [SDWebImageManager.sharedManager.imageDownloader setValue:@"SDWebImage Demo" forHTTPHeaderField:@"AppName"];
    SDWebImageManager.sharedManager.imageDownloader.executionOrder = SDWebImageDownloaderLIFOExecutionOrder;// 后进先出(栈)
    return self;
}

// 刷新缓存
- (void)flushCache
{
    // 清理内存
    [SDWebImageManager.sharedManager.imageCache clearMemory];
    // 清理磁盘
    [SDWebImageManager.sharedManager.imageCache clearDisk];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

#pragma mark - Table View

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return _objects.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }

    [cell.imageView setShowActivityIndicatorView:YES];
    [cell.imageView setIndicatorStyle:UIActivityIndicatorViewStyleGray];

    cell.textLabel.text = [NSString stringWithFormat:@"Image #%ld", (long)indexPath.row];
    cell.imageView.contentMode = UIViewContentModeScaleAspectFill;
    // 设置了网络图像
    [cell.imageView sd_setImageWithURL:[NSURL URLWithString:[_objects objectAtIndex:indexPath.row]]
                      placeholderImage:[UIImage imageNamed:@"placeholder"] options:indexPath.row == 0 ? SDWebImageRefreshCached : 0];

    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (!self.detailViewController)
    {
        self.detailViewController = [[DetailViewController alloc] initWithNibName:@"DetailViewController" bundle:nil];
    }
    NSString *largeImageURL = [[_objects objectAtIndex:indexPath.row] stringByReplacingOccurrencesOfString:@"small" withString:@"source"];
    self.detailViewController.imageURL = [NSURL URLWithString:largeImageURL];
    [self.navigationController pushViewController:self.detailViewController animated:YES];
}

@end

2.对SDWebImageManage进行分析
这里写图片描述

我们知道在数据交互期间,cpu和内存条和磁盘直接的关系

这里写图片描述

所以数据存储在内存条当中,cpu调度会快一些。

SDWebImageManager.h

/*
 * This file is part of the SDWebImage package.
 * (c) Olivier Poitrey <rs@dailymotion.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

#import "SDWebImageCompat.h"
#import "SDWebImageOperation.h"
#import "SDWebImageDownloader.h"
#import "SDImageCache.h"

//枚举
//NS_ENUM和NS_OPTIONS的区别
/*
 1.NSInteger(ENUM),OPTIONS(NSUInteger)
 2.一个是整型的,一个位移枚举
 3.如果是options它可以是多个一起使用,enum只能单独进行使用
 4.编译方式不一样,options C++ NSUInteger
 */
/*
 typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {//单独使用
 UIViewAnimationTransitionNone,
 UIViewAnimationTransitionFlipFromLeft,
 UIViewAnimationTransitionFlipFromRight,
 UIViewAnimationTransitionCurlUp,
 UIViewAnimationTransitionCurlDown,
 };

 typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {//按位或使用
 UIViewAutoresizingNone                 = 0,
 UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
 UIViewAutoresizingFlexibleWidth        = 1 << 1,
 UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
 UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
 UIViewAutoresizingFlexibleHeight       = 1 << 4,
 UIViewAutoresizingFlexibleBottomMargin = 1 << 5
 };
 */
typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
    /**
     * By default, when a URL fail to be downloaded, the URL is blacklisted so the library won't keep trying.
     * 默认情况下,当一个 URL 下载失败,该URL被列入黑名单,将不会继续尝试下载

     * This flag disable this blacklisting.
     * 此标志取消黑名单

     */
    SDWebImageRetryFailed = 1 << 0,//下载失败后还会重新去尝试下载

    /**
     * By default, image downloads are started during UI interactions, this flags disable this feature,
     * 默认情况下,在 UI 交互时也会启动图像下载,此标记取消这一功能

     * leading to delayed download on UIScrollView deceleration for instance.
     * 会延迟下载,UIScrollView停止滚动之后再继续下载
     * 下载事件监听的运行循环模式是 NSDefaultRunLoopMode 默认模式下,UI交互时,不再进行下载
     */
    SDWebImageLowPriority = 1 << 1,

    /**
     * This flag disables on-disk caching
     * 禁用磁盘缓存
     */
    SDWebImageCacheMemoryOnly = 1 << 2, // 只进行内存缓存

    /**
     * This flag enables progressive download, the image is displayed progressively during download as a browser would do.
     * 此标记允许渐进式下载,就像浏览器中那样,下载过程中,图像会逐步显示出来

     * By default, the image is only displayed once completely downloaded.
     * 默认情况下,图像只会在下载完后显示

     */
    SDWebImageProgressiveDownload = 1 << 3,

    /**
     * Even if the image is cached, respect the HTTP response cache control, and refresh the image from remote location if needed.
     * 即使图像被缓存,遵守 HTPP 响应的缓存控制,如果需要,从远程刷新图像

     * The disk caching will be handled by NSURLCache instead of SDWebImage leading to slight performance degradation.
     * 磁盘缓存将由 NSURLCache 处理,而不是 SDWebImage,这会对性能有轻微的影响

     * This option helps deal with images changing behind the same request URL, e.g. Facebook graph api profile pics.
     * 此选项有助于处理同一个请求 URL 的图像发生变化

     * If a cached image is refreshed, the completion block is called once with the cached image and again with the final image.
     * 如果缓存的图像被刷新,会调用一次 completion block,并传递最终的图像

     *
     * Use this flag only if you can't make your URLs static with embedded cache busting parameter.
     * 仅在无法使用嵌入式缓存清理参数确定图像 URL 时,使用此标记

     */
    SDWebImageRefreshCached = 1 << 4, // 刷新
    /**
     * In iOS 4+, continue the download of the image if the app goes to background. This is achieved by asking the system for
     * 在 iOS 4+,当 App 进入后台后仍然会继续下载图像。这是向系统请求额外的后台时间以保证下载请求完成的

     * extra time in background to let the request finish. If the background task expires the operation will be cancelled.
     * 如果后台任务过期,请求将会被取消

     */
    SDWebImageContinueInBackground = 1 << 5,

    /**
     * Handles cookies stored in NSHTTPCookieStore by setting
     * 通过设置

     * NSMutableURLRequest.HTTPShouldHandleCookies = YES;
     * 处理保存在 NSHTTPCookieStore 中的 cookies

     */
    SDWebImageHandleCookies = 1 << 6,//是否管理cookies
    /**
     * Enable to allow untrusted SSL certificates.
     * 允许不信任的 SSL 证书

     * Useful for testing purposes. Use with caution in production.
     * 可以出于测试目的使用,在正式产品中慎用

     */
    SDWebImageAllowInvalidSSLCertificates = 1 << 7,

    /**
     * By default, images are loaded in the order in which they were queued. This flag moves them to
     * 默认情况下,图像会按照在队列中的顺序被加载,此标记会将它们移动到队列前部立即被加载

     * the front of the queue.
     */
    SDWebImageHighPriority = 1 << 8,//延迟占位

    /**
     * By default, placeholder images are loaded while the image is loading. This flag will delay the loading
     * 默认情况下,在加载图像时,占位图像已经会被加载。而此标记会延迟加载占位图像,直到图像已经完成加载

     * of the placeholder image until after the image has finished loading.
     */
    SDWebImageDelayPlaceholder = 1 << 9,
    /**
     * We usually don't call transformDownloadedImage delegate method on animated images,
     * 通常不会在可动画的图像上调用 transformDownloadedImage 代理方法,因为大多数转换代码会破坏动画文件

     * as most transformation code would mangle it.
     * Use this flag to transform them anyway.
     * 使用此标记尝试转换
     */
    SDWebImageTransformAnimatedImage = 1 << 10,// 改变动画图像

    /**
     * By default, image is added to the imageView after download. But in some cases, we want to
     * have the hand before setting the image (apply a filter or add it with cross-fade animation for instance)
     * Use this flag if you want to manually set the image in the completion when success
     */
    SDWebImageAvoidAutoSetImage = 1 << 11  // 避免自动设置图像
};

typedef void(^SDWebImageCompletionBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL);

typedef void(^SDWebImageCompletionWithFinishedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL);

typedef NSString *(^SDWebImageCacheKeyFilterBlock)(NSURL *url);


@class SDWebImageManager;

@protocol SDWebImageManagerDelegate <NSObject>

@optional

/**
 * Controls which image should be downloaded when the image is not found in the cache.
 *
 * @param imageManager The current `SDWebImageManager`
 * @param imageURL     The url of the image to be downloaded
 *
 * @return Return NO to prevent the downloading of the image on cache misses. If not implemented, YES is implied.
 */
- (BOOL)imageManager:(SDWebImageManager *)imageManager shouldDownloadImageForURL:(NSURL *)imageURL;

/**
 * Allows to transform the image immediately after it has been downloaded and just before to cache it on disk and memory.
 * NOTE: This method is called from a global queue in order to not to block the main thread.
 *
 * @param imageManager The current `SDWebImageManager`
 * @param image        The image to transform
 * @param imageURL     The url of the image to transform
 *
 * @return The transformed image object.
 */
- (UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL;

@end

/**
 * The SDWebImageManager is the class behind the UIImageView+WebCache category and likes.
 SDWebImageManager 是 UIImageView+WebCache 等分类后台工作的类
 * It ties the asynchronous downloader (SDWebImageDownloader) with the image cache store (SDImageCache).
 是异步的下载SDWebImageDownloader 和图像缓存存储SDImageCache之间的媒介
 * You can use this class directly to benefit from web image downloading with caching in another context than
 * a UIView.
 *
 * Here is a simple example of how to use SDWebImageManager:
 *
 * @code

SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadImageWithURL:imageURL
                      options:0
                     progress:nil
                    completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
                        if (image) {
                            // do something with image
                        }
                    }];

 * @endcode
 */
@interface SDWebImageManager : NSObject

@property (weak, nonatomic) id <SDWebImageManagerDelegate> delegate;

@property (strong, nonatomic, readonly) SDImageCache *imageCache;
@property (strong, nonatomic, readonly) SDWebImageDownloader *imageDownloader;

/**
 * The cache filter is a block used each time SDWebImageManager need to convert an URL into a cache key. This can
 * be used to remove dynamic part of an image URL.
 *
 * The following example sets a filter in the application delegate that will remove any query-string from the
 * URL before to use it as a cache key:
 *
 * @code

[[SDWebImageManager sharedManager] setCacheKeyFilter:^(NSURL *url) {
    url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];
    return [url absoluteString];
}];

 * @endcode
 */
@property (nonatomic, copy) SDWebImageCacheKeyFilterBlock cacheKeyFilter;

/**
 * Returns global SDWebImageManager instance.
 *
 * @return SDWebImageManager shared instance
 */
+ (SDWebImageManager *)sharedManager;

/**
 * Allows to specify instance of cache and image downloader used with image manager.
 * @return new instance of `SDWebImageManager` with specified cache and downloader.
 */
- (instancetype)initWithCache:(SDImageCache *)cache downloader:(SDWebImageDownloader *)downloader;

/**
 * Downloads the image at the given URL if not present in cache or return the cached version otherwise.
 *
 * @param url            The URL to the image
 * @param options        A mask to specify options to use for this request
 * @param progressBlock  A block called while image is downloading
 * @param completedBlock A block called when operation has been completed.
 *
 *   This parameter is required.
 * 
 *   This block has no return value and takes the requested UIImage as first parameter.
 *   In case of error the image parameter is nil and the second parameter may contain an NSError.
 *
 *   The third parameter is an `SDImageCacheType` enum indicating if the image was retrieved from the local cache
 *   or from the memory cache or from the network.
 *
 *   The last parameter is set to NO when the SDWebImageProgressiveDownload option is used and the image is 
 *   downloading. This block is thus called repeatedly with a partial image. When image is fully downloaded, the
 *   block is called a last time with the full image and the last parameter set to YES.
 *
 * @return Returns an NSObject conforming to SDWebImageOperation. Should be an instance of SDWebImageDownloaderOperation
 */
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
                                         options:(SDWebImageOptions)options
                                        progress:(SDWebImageDownloaderProgressBlock)progressBlock
                                       completed:(SDWebImageCompletionWithFinishedBlock)completedBlock;

/**
 * Saves image to cache for given URL
 *
 * @param image The image to cache
 * @param url   The URL to the image
 *
 */

- (void)saveImageToCache:(UIImage *)image forURL:(NSURL *)url;

/**
 * Cancel all current operations
 */
- (void)cancelAll;

/**
 * Check one or more operations running
 */
- (BOOL)isRunning;

/**
 *  Check if image has already been cached
 *
 *  @param url image url
 *
 *  @return if the image was already cached
 */
- (BOOL)cachedImageExistsForURL:(NSURL *)url;

/**
 *  Check if image has already been cached on disk only
 *
 *  @param url image url
 *
 *  @return if the image was already cached (disk only)
 */
- (BOOL)diskImageExistsForURL:(NSURL *)url;

/**
 *  Async check if image has already been cached
 *
 *  @param url              image url
 *  @param completionBlock  the block to be executed when the check is finished
 *  
 *  @note the completion block is always executed on the main queue
 */
- (void)cachedImageExistsForURL:(NSURL *)url
                     completion:(SDWebImageCheckCacheCompletionBlock)completionBlock;

/**
 *  Async check if image has already been cached on disk only
 *
 *  @param url              image url
 *  @param completionBlock  the block to be executed when the check is finished
 *
 *  @note the completion block is always executed on the main queue
 */
- (void)diskImageExistsForURL:(NSURL *)url
                   completion:(SDWebImageCheckCacheCompletionBlock)completionBlock;


/**
 *Return the cache key for a given URL
 */
- (NSString *)cacheKeyForURL:(NSURL *)url;

@end


#pragma mark - Deprecated

typedef void(^SDWebImageCompletedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType) __deprecated_msg("Block type deprecated. Use `SDWebImageCompletionBlock`");
typedef void(^SDWebImageCompletedWithFinishedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished) __deprecated_msg("Block type deprecated. Use `SDWebImageCompletionWithFinishedBlock`");


@interface SDWebImageManager (Deprecated)

/**
 *  Downloads the image at the given URL if not present in cache or return the cached version otherwise.
 *
 *  @deprecated This method has been deprecated. Use `downloadImageWithURL:options:progress:completed:`
 */
- (id <SDWebImageOperation>)downloadWithURL:(NSURL *)url
                                    options:(SDWebImageOptions)options
                                   progress:(SDWebImageDownloaderProgressBlock)progressBlock
                                  completed:(SDWebImageCompletedWithFinishedBlock)completedBlock __deprecated_msg("Method deprecated. Use `downloadImageWithURL:options:progress:completed:`");

@end

3.学习SD缓存机制
这里写图片描述

我们首先看一个NSCache的Demo

//
//  ViewController.m
//  NSCacheDemo
//
//  Created by mac on 2018/6/4.
//  Copyright © 2018年 mac. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()<NSCacheDelegate>

@end

@implementation ViewController{
    NSCache * _cache;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    _cache =[[NSCache alloc]init];
    //设置上限
    _cache.countLimit = 10;
    _cache.delegate = self;
}


- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    for (int i=0; i< 20; i++) {
        NSString *str =[@"liSi" stringByAppendingFormat:@"%d",i];
        //添加到缓存
        [_cache setObject:str forKey:@(i)];
        NSLog(@"add---%@",[_cache objectForKey:@(i)]);
    }
}
//查看缓存中的内容
- (IBAction)showCache:(id)sender {
    for (int i=0; i< 20; i++) {
       id obj = [_cache objectForKey:@(i)];
        NSLog(@"---shuchu-- %@",obj);
    }
}

-(void)cache:(NSCache *)cache willEvictObject:(id)obj{
    NSLog(@"-----yichu-- %@",obj);
}
@end

logo日志

2018-06-04 11:35:53.490902+0800 NSCacheDemo[3506:108820] add---liSi0
2018-06-04 11:35:53.491661+0800 NSCacheDemo[3506:108820] add---liSi1
2018-06-04 11:35:53.494140+0800 NSCacheDemo[3506:108820] add---liSi2
2018-06-04 11:35:53.496123+0800 NSCacheDemo[3506:108820] add---liSi3
2018-06-04 11:35:53.496945+0800 NSCacheDemo[3506:108820] add---liSi4
2018-06-04 11:35:53.497364+0800 NSCacheDemo[3506:108820] add---liSi5
2018-06-04 11:35:53.497750+0800 NSCacheDemo[3506:108820] add---liSi6
2018-06-04 11:35:53.498342+0800 NSCacheDemo[3506:108820] add---liSi7
2018-06-04 11:35:53.498812+0800 NSCacheDemo[3506:108820] add---liSi8
2018-06-04 11:35:53.499065+0800 NSCacheDemo[3506:108820] add---liSi9
2018-06-04 11:35:53.499266+0800 NSCacheDemo[3506:108820] -----yichu-- liSi0
2018-06-04 11:35:53.499957+0800 NSCacheDemo[3506:108820] add---liSi10
2018-06-04 11:35:53.500377+0800 NSCacheDemo[3506:108820] -----yichu-- liSi1
2018-06-04 11:35:53.500995+0800 NSCacheDemo[3506:108820] add---liSi11
2018-06-04 11:35:53.501294+0800 NSCacheDemo[3506:108820] -----yichu-- liSi2
2018-06-04 11:35:53.501452+0800 NSCacheDemo[3506:108820] add---liSi12
2018-06-04 11:35:53.502334+0800 NSCacheDemo[3506:108820] -----yichu-- liSi3
2018-06-04 11:35:53.502663+0800 NSCacheDemo[3506:108820] add---liSi13
2018-06-04 11:35:53.503345+0800 NSCacheDemo[3506:108820] -----yichu-- liSi4
2018-06-04 11:35:53.503578+0800 NSCacheDemo[3506:108820] add---liSi14
2018-06-04 11:35:53.504137+0800 NSCacheDemo[3506:108820] -----yichu-- liSi5
2018-06-04 11:35:53.504466+0800 NSCacheDemo[3506:108820] add---liSi15
2018-06-04 11:35:53.504817+0800 NSCacheDemo[3506:108820] -----yichu-- liSi6
2018-06-04 11:35:53.505076+0800 NSCacheDemo[3506:108820] add---liSi16
2018-06-04 11:35:53.505610+0800 NSCacheDemo[3506:108820] -----yichu-- liSi7
2018-06-04 11:35:53.505731+0800 NSCacheDemo[3506:108820] add---liSi17
2018-06-04 11:35:53.505995+0800 NSCacheDemo[3506:108820] -----yichu-- liSi8
2018-06-04 11:35:53.506512+0800 NSCacheDemo[3506:108820] add---liSi18
2018-06-04 11:35:53.507336+0800 NSCacheDemo[3506:108820] -----yichu-- liSi9
2018-06-04 11:35:53.507526+0800 NSCacheDemo[3506:108820] add---liSi19
2018-06-04 11:35:55.839001+0800 NSCacheDemo[3506:108820] ---shuchu-- (null)
2018-06-04 11:35:55.839292+0800 NSCacheDemo[3506:108820] ---shuchu-- (null)
2018-06-04 11:35:55.839543+0800 NSCacheDemo[3506:108820] ---shuchu-- (null)
2018-06-04 11:35:55.839725+0800 NSCacheDemo[3506:108820] ---shuchu-- (null)
2018-06-04 11:35:55.839924+0800 NSCacheDemo[3506:108820] ---shuchu-- (null)
2018-06-04 11:35:55.840052+0800 NSCacheDemo[3506:108820] ---shuchu-- (null)
2018-06-04 11:35:55.840165+0800 NSCacheDemo[3506:108820] ---shuchu-- (null)
2018-06-04 11:35:55.840476+0800 NSCacheDemo[3506:108820] ---shuchu-- (null)
2018-06-04 11:35:55.840627+0800 NSCacheDemo[3506:108820] ---shuchu-- (null)
2018-06-04 11:35:55.840900+0800 NSCacheDemo[3506:108820] ---shuchu-- (null)
2018-06-04 11:35:55.841306+0800 NSCacheDemo[3506:108820] ---shuchu-- liSi10
2018-06-04 11:35:55.841643+0800 NSCacheDemo[3506:108820] ---shuchu-- liSi11
2018-06-04 11:35:55.842014+0800 NSCacheDemo[3506:108820] ---shuchu-- liSi12
2018-06-04 11:35:55.842384+0800 NSCacheDemo[3506:108820] ---shuchu-- liSi13
2018-06-04 11:35:55.842673+0800 NSCacheDemo[3506:108820] ---shuchu-- liSi14
2018-06-04 11:35:55.843006+0800 NSCacheDemo[3506:108820] ---shuchu-- liSi15
2018-06-04 11:35:55.843331+0800 NSCacheDemo[3506:108820] ---shuchu-- liSi16
2018-06-04 11:35:55.843684+0800 NSCacheDemo[3506:108820] ---shuchu-- liSi17
2018-06-04 11:35:55.843972+0800 NSCacheDemo[3506:108820] ---shuchu-- liSi18
2018-06-04 11:35:55.844492+0800 NSCacheDemo[3506:108820] ---shuchu-- liSi19

假如我们设置cahe的最大缓存数量的话且数据超出最大缓存数。系统将会把先存的数据清理,保存后面的数据。

转过来我们看SDWebImage的SDImageCache

/*
 * This file is part of the SDWebImage package.
 * (c) Olivier Poitrey <rs@dailymotion.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

#import "SDImageCache.h"
#import "SDWebImageDecoder.h"
#import "UIImage+MultiFormat.h"
#import <CommonCrypto/CommonDigest.h>

// See https://github.com/rs/SDWebImage/pull/1141 for discussion
@interface AutoPurgeCache : NSCache
@end

@implementation AutoPurgeCache

- (id)init
{
    self = [super init];
    if (self) {

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeAllObjects) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
    }
    return self;
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
}

@end

static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
// PNG signature bytes and data (below)
static unsigned char kPNGSignatureBytes[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
static NSData *kPNGSignatureData = nil;

BOOL ImageDataHasPNGPreffix(NSData *data);

BOOL ImageDataHasPNGPreffix(NSData *data) {
    NSUInteger pngSignatureLength = [kPNGSignatureData length];
    if ([data length] >= pngSignatureLength) {
        if ([[data subdataWithRange:NSMakeRange(0, pngSignatureLength)] isEqualToData:kPNGSignatureData]) {
            return YES;
        }
    }

    return NO;
}

FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
    return image.size.height * image.size.width * image.scale * image.scale;
}

@interface SDImageCache ()

@property (strong, nonatomic) NSCache *memCache;
@property (strong, nonatomic) NSString *diskCachePath;
@property (strong, nonatomic) NSMutableArray *customPaths;
@property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t ioQueue;

@end


@implementation SDImageCache {
    NSFileManager *_fileManager;
}

+ (SDImageCache *)sharedImageCache {
    static dispatch_once_t once;
    static id instance;
    dispatch_once(&once, ^{
        instance = [self new];
    });
    return instance;
}

- (id)init {
    return [self initWithNamespace:@"default"];
}

- (id)initWithNamespace:(NSString *)ns {
    NSString *path = [self makeDiskCachePath:ns];
    return [self initWithNamespace:ns diskCacheDirectory:path];
}

- (id)initWithNamespace:(NSString *)ns diskCacheDirectory:(NSString *)directory {
    if ((self = [super init])) {
        NSString *fullNamespace = [@"com.hackemist.SDWebImageCache." stringByAppendingString:ns];

        // initialise PNG signature data
        kPNGSignatureData = [NSData dataWithBytes:kPNGSignatureBytes length:8];

        // Create IO serial queue
        _ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL);

        // Init default values 
        _maxCacheAge = kDefaultCacheMaxCacheAge;  // 一周

        // Init the memory cache // 自动清除缓存
        _memCache = [[AutoPurgeCache alloc] init];
        _memCache.name = fullNamespace;

        // Init the disk cache
        if (directory != nil) {
            _diskCachePath = [directory stringByAppendingPathComponent:fullNamespace];
        } else {
            NSString *path = [self makeDiskCachePath:ns];
            _diskCachePath = path;
        }

        // Set decompression to YES
        _shouldDecompressImages = YES;

        // memory cache enabled
        _shouldCacheImagesInMemory = YES;

        // Disable iCloud
        _shouldDisableiCloud = YES;

        dispatch_sync(_ioQueue, ^{
            _fileManager = [NSFileManager new];
        });

#if TARGET_OS_IOS
        // Subscribe to app events
        // 清理内存
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(clearMemory)
                                                     name:UIApplicationDidReceiveMemoryWarningNotification
                                                   object:nil];
        // 清理磁盘
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(cleanDisk)
                                                     name:UIApplicationWillTerminateNotification
                                                   object:nil];
        // 后台清理磁盘
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(backgroundCleanDisk)
                                                     name:UIApplicationDidEnterBackgroundNotification
                                                   object:nil];
#endif
    }

    return self;
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    SDDispatchQueueRelease(_ioQueue);
}

- (void)addReadOnlyCachePath:(NSString *)path {
    if (!self.customPaths) {
        self.customPaths = [NSMutableArray new];
    }

    if (![self.customPaths containsObject:path]) {
        [self.customPaths addObject:path];
    }
}

- (NSString *)cachePathForKey:(NSString *)key inPath:(NSString *)path {
    NSString *filename = [self cachedFileNameForKey:key];
    return [path stringByAppendingPathComponent:filename];
}

- (NSString *)defaultCachePathForKey:(NSString *)key {
    return [self cachePathForKey:key inPath:self.diskCachePath];
}

#pragma mark SDImageCache (private)
- (NSString *)cachedFileNameForKey:(NSString *)key {
    const char *str = [key UTF8String];
    if (str == NULL) {
        str = "";
    }
    // 保存缓存文件的文件名 : url做了md5的处理 + .扩展名
    unsigned char r[CC_MD5_DIGEST_LENGTH];
    CC_MD5(str, (CC_LONG)strlen(str), r);
    NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%@",
                          r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10],
                          r[11], r[12], r[13], r[14], r[15], [[key pathExtension] isEqualToString:@""] ? @"" : [NSString stringWithFormat:@".%@", [key pathExtension]]];

    return filename;
}

#pragma mark ImageCache
// Init the disk cache
-(NSString *)makeDiskCachePath:(NSString*)fullNamespace{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    return [paths[0] stringByAppendingPathComponent:fullNamespace];
}

- (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk {
    if (!image || !key) {
        return;
    }
    // if memory cache is enabled
    if (self.shouldCacheImagesInMemory) {
        NSUInteger cost = SDCacheCostForImage(image);
        [self.memCache setObject:image forKey:key cost:cost];
    }

    if (toDisk) {
        dispatch_async(self.ioQueue, ^{
            NSData *data = imageData;

            if (image && (recalculate || !data)) {
#if TARGET_OS_IPHONE
                // We need to determine if the image is a PNG or a JPEG
                // PNGs are easier to detect because they have a unique signature (http://www.w3.org/TR/PNG-Structure.html)
                // The first eight bytes of a PNG file always contain the following (decimal) values:
                // 137 80 78 71 13 10 26 10

                // If the imageData is nil (i.e. if trying to save a UIImage directly or the image was transformed on download)
                // and the image has an alpha channel, we will consider it PNG to avoid losing the transparency
                int alphaInfo = CGImageGetAlphaInfo(image.CGImage);
                BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||
                                  alphaInfo == kCGImageAlphaNoneSkipFirst ||
                                  alphaInfo == kCGImageAlphaNoneSkipLast);
                BOOL imageIsPng = hasAlpha;

                // But if we have an image data, we will look at the preffix
                if ([imageData length] >= [kPNGSignatureData length]) {
                    imageIsPng = ImageDataHasPNGPreffix(imageData);
                }

                if (imageIsPng) {
                    data = UIImagePNGRepresentation(image);
                }
                else {
                    data = UIImageJPEGRepresentation(image, (CGFloat)1.0);
                }
#else
                data = [NSBitmapImageRep representationOfImageRepsInArray:image.representations usingType: NSJPEGFileType properties:nil];
#endif
            }

            [self storeImageDataToDisk:data forKey:key];
        });
    }
}

- (void)storeImage:(UIImage *)image forKey:(NSString *)key {
    [self storeImage:image recalculateFromImage:YES imageData:nil forKey:key toDisk:YES];
}

- (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk {
    [self storeImage:image recalculateFromImage:YES imageData:nil forKey:key toDisk:toDisk];
}

- (void)storeImageDataToDisk:(NSData *)imageData forKey:(NSString *)key {

    if (!imageData) {
        return;
    }

    if (![_fileManager fileExistsAtPath:_diskCachePath]) {
        [_fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];
    }

    // get cache Path for image key
    NSString *cachePathForKey = [self defaultCachePathForKey:key];
    // transform to NSUrl
    NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey];

    [_fileManager createFileAtPath:cachePathForKey contents:imageData attributes:nil];

    // disable iCloud backup
    if (self.shouldDisableiCloud) {
        [fileURL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:nil];
    }
}

- (BOOL)diskImageExistsWithKey:(NSString *)key {
    BOOL exists = NO;

    // this is an exception to access the filemanager on another queue than ioQueue, but we are using the shared instance
    // from apple docs on NSFileManager: The methods of the shared NSFileManager object can be called from multiple threads safely.
    exists = [[NSFileManager defaultManager] fileExistsAtPath:[self defaultCachePathForKey:key]];

    // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name
    // checking the key with and without the extension
    if (!exists) {
        exists = [[NSFileManager defaultManager] fileExistsAtPath:[[self defaultCachePathForKey:key] stringByDeletingPathExtension]];
    }

    return exists;
}

- (void)diskImageExistsWithKey:(NSString *)key completion:(SDWebImageCheckCacheCompletionBlock)completionBlock {
    dispatch_async(_ioQueue, ^{
        BOOL exists = [_fileManager fileExistsAtPath:[self defaultCachePathForKey:key]];

        // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name
        // checking the key with and without the extension
        if (!exists) {
            exists = [_fileManager fileExistsAtPath:[[self defaultCachePathForKey:key] stringByDeletingPathExtension]];
        }

        if (completionBlock) {
            dispatch_async(dispatch_get_main_queue(), ^{
                completionBlock(exists);
            });
        }
    });
}

- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key {
    return [self.memCache objectForKey:key];
}

- (UIImage *)imageFromDiskCacheForKey:(NSString *)key {

    // First check the in-memory cache...
    UIImage *image = [self imageFromMemoryCacheForKey:key];
    if (image) {
        return image;
    }

    // Second check the disk cache...
    UIImage *diskImage = [self diskImageForKey:key];
    if (diskImage && self.shouldCacheImagesInMemory) {
        NSUInteger cost = SDCacheCostForImage(diskImage);
        [self.memCache setObject:diskImage forKey:key cost:cost];
    }

    return diskImage;
}

- (NSData *)diskImageDataBySearchingAllPathsForKey:(NSString *)key {
    NSString *defaultPath = [self defaultCachePathForKey:key];
    NSData *data = [NSData dataWithContentsOfFile:defaultPath];
    if (data) {
        return data;
    }

    // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name
    // checking the key with and without the extension
    data = [NSData dataWithContentsOfFile:[defaultPath stringByDeletingPathExtension]];
    if (data) {
        return data;
    }

    NSArray *customPaths = [self.customPaths copy];
    for (NSString *path in customPaths) {
        NSString *filePath = [self cachePathForKey:key inPath:path];
        NSData *imageData = [NSData dataWithContentsOfFile:filePath];
        if (imageData) {
            return imageData;
        }

        // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name
        // checking the key with and without the extension
        imageData = [NSData dataWithContentsOfFile:[filePath stringByDeletingPathExtension]];
        if (imageData) {
            return imageData;
        }
    }

    return nil;
}

- (UIImage *)diskImageForKey:(NSString *)key {
    NSData *data = [self diskImageDataBySearchingAllPathsForKey:key];
    if (data) {
        UIImage *image = [UIImage sd_imageWithData:data];
        image = [self scaledImageForKey:key image:image];
        if (self.shouldDecompressImages) {
            image = [UIImage decodedImageWithImage:image];
        }
        return image;
    }
    else {
        return nil;
    }
}

- (UIImage *)scaledImageForKey:(NSString *)key image:(UIImage *)image {
    return SDScaledImageForKey(key, image);
}

- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock {
    if (!doneBlock) {
        return nil;
    }

    if (!key) {
        doneBlock(nil, SDImageCacheTypeNone);
        return nil;
    }

    // First check the in-memory cache...
    UIImage *image = [self imageFromMemoryCacheForKey:key];
    if (image) {
        doneBlock(image, SDImageCacheTypeMemory);
        return nil;
    }

    NSOperation *operation = [NSOperation new];
    dispatch_async(self.ioQueue, ^{
        if (operation.isCancelled) {
            return;
        }

        @autoreleasepool {
            UIImage *diskImage = [self diskImageForKey:key];
            if (diskImage && self.shouldCacheImagesInMemory) {
                NSUInteger cost = SDCacheCostForImage(diskImage);
                [self.memCache setObject:diskImage forKey:key cost:cost];
            }

            dispatch_async(dispatch_get_main_queue(), ^{
                doneBlock(diskImage, SDImageCacheTypeDisk);
            });
        }
    });

    return operation;
}

- (void)removeImageForKey:(NSString *)key {
    [self removeImageForKey:key withCompletion:nil];
}

- (void)removeImageForKey:(NSString *)key withCompletion:(SDWebImageNoParamsBlock)completion {
    [self removeImageForKey:key fromDisk:YES withCompletion:completion];
}

- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk {
    [self removeImageForKey:key fromDisk:fromDisk withCompletion:nil];
}

- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(SDWebImageNoParamsBlock)completion {

    if (key == nil) {
        return;
    }

    if (self.shouldCacheImagesInMemory) {
        [self.memCache removeObjectForKey:key];
    }

    if (fromDisk) {
        dispatch_async(self.ioQueue, ^{
            [_fileManager removeItemAtPath:[self defaultCachePathForKey:key] error:nil];

            if (completion) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    completion();
                });
            }
        });
    } else if (completion){
        completion();
    }

}

- (void)setMaxMemoryCost:(NSUInteger)maxMemoryCost {
    self.memCache.totalCostLimit = maxMemoryCost;
}

- (NSUInteger)maxMemoryCost {
    return self.memCache.totalCostLimit;
}

- (NSUInteger)maxMemoryCountLimit {
    return self.memCache.countLimit;
}

- (void)setMaxMemoryCountLimit:(NSUInteger)maxCountLimit {
    self.memCache.countLimit = maxCountLimit;
}

- (void)clearMemory {
    [self.memCache removeAllObjects];
}
// 清空磁盘
- (void)clearDisk {
    [self clearDiskOnCompletion:nil];
}


- (void)clearDiskOnCompletion:(SDWebImageNoParamsBlock)completion
{
    // 删除缓存目录
    dispatch_async(self.ioQueue, ^{
        [_fileManager removeItemAtPath:self.diskCachePath error:nil];
        // 创建同名的缓存目录
        [_fileManager createDirectoryAtPath:self.diskCachePath
                withIntermediateDirectories:YES
                                 attributes:nil
                                      error:NULL];

        if (completion) {
            dispatch_async(dispatch_get_main_queue(), ^{
                completion();
            });
        }
    });
}
// 清理磁盘
- (void)cleanDisk {
    [self cleanDiskWithCompletionBlock:nil];
}

- (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock {
    dispatch_async(self.ioQueue, ^{
        // 1. 找磁盘缓存目录
        NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];
        /* 2.
         NSURLIsDirectoryKey, 目录key
         NSURLContentModificationDateKey, url指向的内容,最后修改时间的key
         NSURLTotalFileAllocatedSizeKey 文件总大小的key
         */
        NSArray *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey];

        // 3.Enumerator(链表的数据结构存在的) 迭代器的设计模式
        // 创建目录文件的枚举器
        /*
         NSDirectoryEnumerationSkipsHiddenFiles  不遍历隐藏文件
         NSDirectoryEnumerationSkipsSubdirectoryDescendants  不要下载到目录中
         NSDirectoryEnumerationSkipsPackageDescendants 不要下到包
         */
        NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL
                                                   includingPropertiesForKeys:resourceKeys
                                                                      options:NSDirectoryEnumerationSkipsHiddenFiles
                                                                 errorHandler:NULL];

        // 4.从现在开始,减去最大时间
        NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.maxCacheAge];

        // 5.定义了一个字典
        NSMutableDictionary *cacheFiles = [NSMutableDictionary dictionary];
        // 6.定义了当前缓存的大小
        NSUInteger currentCacheSize = 0;

        // Enumerate all of the files in the cache directory.  This loop has two purposes:
        //
        //  1. Removing files that are older than the expiration date.
        //  2. Storing file attributes for the size-based cleanup pass.
        // 7. 创建一个可变数组
        NSMutableArray *urlsToDelete = [[NSMutableArray alloc] init];

        // 8.拿到具体的URL
        /*
            1.清除超过过期时间的文件
            2.以大小为基础进行第二轮文件保存
         */
        for (NSURL *fileURL in fileEnumerator) {
            // 9.获取指定的key的信息, 以字典的方式返回
            NSDictionary *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:NULL];

            // Skip directories.
            // 10. 判断这个目录,有就直接跳过
            if ([resourceValues[NSURLIsDirectoryKey] boolValue]) {
                continue;
            }

            // Remove files that are older than the expiration date;
            // 11.拿到文件的修改日期
            NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];

            // 12.判断是不是最后修改,是最后修改,就添加到数组
            if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
                [urlsToDelete addObject:fileURL];
                continue;
            }

            // Store a reference to this file and account for its total size.
            // 13.对未过期的文件, 以键值的形式存起来,方便后面重置缓存
            NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
            currentCacheSize += [totalAllocatedSize unsignedIntegerValue];
            [cacheFiles setObject:resourceValues forKey:fileURL];
        }
        // 14. 直接删除
        for (NSURL *fileURL in urlsToDelete) {
            [_fileManager removeItemAtURL:fileURL error:nil];
        }

        // If our remaining disk cache exceeds a configured maximum size, perform a second
        // size-based cleanup pass.  We delete the oldest files first.
        // 15.有一个上限,如果自己设置了上限,才会走if分支
        if (self.maxCacheSize > 0 && currentCacheSize > self.maxCacheSize) {
            // Target half of our maximum cache size for this cleanup pass.
            // 16. 所需的缓存大小一般的时候
            const NSUInteger desiredCacheSize = self.maxCacheSize / 2;

            // Sort the remaining cache files by their last modification time (oldest first).
            // 把上次缓存的文件进行排序, 最早进来的排第一
            /*NSSortConcurrent 并行的方式
             */
            NSArray *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent
                                                            usingComparator:^NSComparisonResult(id obj1, id obj2) {
                                                                return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]];
                                                            }];

            // Delete files until we fall below our desired cache size.
            // 遍历删除文件.直到低于缓存大小
            for (NSURL *fileURL in sortedFiles) {
                if ([_fileManager removeItemAtURL:fileURL error:nil]) {
                    NSDictionary *resourceValues = cacheFiles[fileURL];
                    NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
                    currentCacheSize -= [totalAllocatedSize unsignedIntegerValue];

                    if (currentCacheSize < desiredCacheSize) {
                        break;
                    }
                }
            }
        }
        if (completionBlock) {
            dispatch_async(dispatch_get_main_queue(), ^{
                completionBlock();
            });
        }
    });
}

- (void)backgroundCleanDisk {
    Class UIApplicationClass = NSClassFromString(@"UIApplication");
    if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
        return;
    }
    UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)];
    __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
        // Clean up any unfinished task business by marking where you
        // stopped or ending the task outright.
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];

    // Start the long-running task and return immediately.
    [self cleanDiskWithCompletionBlock:^{
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
}

- (NSUInteger)getSize {
    __block NSUInteger size = 0;
    dispatch_sync(self.ioQueue, ^{
        NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtPath:self.diskCachePath];
        for (NSString *fileName in fileEnumerator) {
            NSString *filePath = [self.diskCachePath stringByAppendingPathComponent:fileName];
            NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
            size += [attrs fileSize];
        }
    });
    return size;
}

- (NSUInteger)getDiskCount {
    __block NSUInteger count = 0;
    dispatch_sync(self.ioQueue, ^{
        NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtPath:self.diskCachePath];
        count = [[fileEnumerator allObjects] count];
    });
    return count;
}

- (void)calculateSizeWithCompletionBlock:(SDWebImageCalculateSizeBlock)completionBlock {
    NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];

    dispatch_async(self.ioQueue, ^{
        NSUInteger fileCount = 0;
        NSUInteger totalSize = 0;

        NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL
                                                   includingPropertiesForKeys:@[NSFileSize]
                                                                      options:NSDirectoryEnumerationSkipsHiddenFiles
                                                                 errorHandler:NULL];

        for (NSURL *fileURL in fileEnumerator) {
            NSNumber *fileSize;
            [fileURL getResourceValue:&fileSize forKey:NSURLFileSizeKey error:NULL];
            totalSize += [fileSize unsignedIntegerValue];
            fileCount += 1;
        }

        if (completionBlock) {
            dispatch_async(dispatch_get_main_queue(), ^{
                completionBlock(fileCount, totalSize);
            });
        }
    });
}

@end

总的操作步骤是:
1、先清除已超过最大缓存时间的缓存文件
2、保存缓存文件的大小
3、判断你的上限,进行第二轮的清除
4、默认的清除方式,先删除老的,达到期望缓存的大小,最大缓存的一半。

如何区分不同格式的图像

1、png:无损压缩,解压缩性能高
2、jpg:压缩比很高
3、gif:动图
4、webp:压缩比比jpg小1/3
5、TIF

这里写图片描述

static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
// PNG signature bytes and data (below)
static unsigned char kPNGSignatureBytes[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
static NSData *kPNGSignatureData = nil;

BOOL ImageDataHasPNGPreffix(NSData *data);

BOOL ImageDataHasPNGPreffix(NSData *data) {
    NSUInteger pngSignatureLength = [kPNGSignatureData length];
    if ([data length] >= pngSignatureLength) {
        if ([[data subdataWithRange:NSMakeRange(0, pngSignatureLength)] isEqualToData:kPNGSignatureData]) {
            return YES;
        }
    }

    return NO;
}

如何保存缓存文件名

#pragma mark SDImageCache (private)
- (NSString *)cachedFileNameForKey:(NSString *)key {
    const char *str = [key UTF8String];
    if (str == NULL) {
        str = "";
    }
    // 保存缓存文件的文件名 : url做了md5的处理 + .扩展名
    unsigned char r[CC_MD5_DIGEST_LENGTH];
    CC_MD5(str, (CC_LONG)strlen(str), r);
    NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%@",
                          r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10],
                          r[11], r[12], r[13], r[14], r[15], [[key pathExtension] isEqualToString:@""] ? @"" : [NSString stringWithFormat:@".%@", [key pathExtension]]];

    return filename;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

iOS架构师_SDWebImage源码分析 的相关文章

  • iOS架构师_观察者模式

    定义 观察者模式 有时又被称为模型 视图 View 模式 源 收听者 Listener 模式或从属者模式 一个目标物件管理所有相依于它的观察者物件 并且在它本身的状态改变时主动发出通知 这通常透过呼叫各观察者所提供的方法来实现 此种模式通常
  • iOS进阶—Runtime基础

    iOS进阶 目录 GitHub参考 RunTime 基础 一个程序的执行过程 大概就是代码 gt 编译链接 gt 执行 C语言 import
  • iOS进阶_WebDav(五.WebDav的上传进度&多线程下载思路)

    WebDav的上传进度 import ViewController h interface ViewController
  • iOS开发_HOOK

    我们在使用NSURLRequest时 传入请求地址URL 但是如果URL中有中文的话 我们会发现NSURLRequest的请求地址会报空 void viewDidLoad super viewDidLoad NSURL url NSURL
  • iOS架构师_架构模式(代理,block,通知,MVC,MVP,MVVM)

    1 什么是架构 没有明确的定义 属于设计的一方面 没明确的把设计和架构进行区分 它可以小到类与类之间的交互 大到不同模块之间 以及不同业务之间的交互 都可以从架构的层面去理解它 所有架构和设计模式的目的都是为了解耦合 2 基本的架构基础 案
  • iOS进阶—Runtime源码解析:动态方法解析

    GitHub参考 PS 参考GitHub分享的objc runtime master代码 及Runtime003代码 iOS进阶 目录 接上文iOS进阶 Runtime源码解析 消息发送 查看Runtime源码 No implementat
  • iOS核心动画CoreAnimation系统进阶(赛贝尔曲线-菜单侧滑动画拆分动画详解)

    我们知道动画是基于绘制的 多次绘制贝塞尔的过程就会形成动画 流畅的动画效果会给用户带来不一样的使用体验 下面我们就让App开发中经常使用到的侧滑动画进行拆分详解 效果图如下 为侧滑动画封装一个slideMenuView 绘制侧滑动画需要下面
  • iOS进阶_多线程(二.线程间的状态)

    ViewController m 004 NSThread状态 Created by mac on 2018 4 27 Copyright 2018年 mac All rights reserved import ViewControlle
  • iOS架构师_SDWebImage源码分析

    1 对SDdemo的分析 针对SD的3 8 2版本进行分析 因为从git上面直接下载SD的源码 项目运行会报错 缺少libwebp这个库 我们下载3 8 2版本 运行 libwebp 是个webp的图片格式 属于谷歌的库 所以只能翻墙去国外
  • iOS自动化布局-AutoLayout约束优先级

    约束的优先级 AutoLayout添加的约束中也有优先级 Priority 优先级的数值1 1000 分为两种情况 一种情况是我们经常添加的各种约束 默认值1000 最大值 优先执行 条件允许的话系统会自动满足我们的约束需求 第二种就是固有
  • iOS进阶_密码学(二.钥匙串访问)

    网络开发中的原则 在网络上不允许传输用户的明文隐私数据 在本地不允许保存用户的明文隐私数据 类似于QQ 微信的记住密码 在客户端本地保存用户加密后的密码 NSUserDefaults 明文保存才能反算 能够反算的算法 钥匙串访问 开放给开发
  • iOS进阶_NSURLConnection(被弃用的原因:Connection的缺点)

    NSURLConnection下载 我们在前面的文章中介绍了MAC电脑搭建Apache服务器 在Sites文件夹中放置一个视频文件MVVM mp4 pbb 通过NSURLConnection进行下载 访问http localhost 我们可
  • iOS进阶_密码学进阶(三.非对称加密算法-RSA公钥私钥的生成)

    应用场景 集成支付宝SDK 会看见 p12文件也有 非对称加密算法 一 运行终端 生成私钥 1 cd Users mac Desktop iOS 加密 2 openssl genrsa out private pem 512 生成私钥 注意
  • 解释SDWebImage代码中__weak和__strong的使用原因

    我认为我很好地理解了强关键字和弱关键字 但我不明白它在下面的代码中是如何使用的 此代码来自 Olivier Poitrey 的 SDWebImage 可在 github 上找到 我了解强关键字和弱关键字 如下所述 iOS5中强存储和弱存储的
  • SDWebImage 使用 Swift 将图像设置为 CellView 中的 UIButton

    您好 我正在使用 SDWebImage 将图像设置为 xcode 10 2 1 中的按钮 我正在尝试以下操作 import SDWebImage cell bProfileImage sd setImage with URL string
  • iO 上有图像缓存框架吗?

    我已经在使用 AFNetworking 进行异步图像下载 我想知道是否有一种方法可以存储在磁盘或核心数据上 我应该研究 SDWebImage 吗 或者您会建议采用定制解决方案 基本上我希望有一种非常透明的方式从 url 获取图像 所以首先在
  • SDWebImage UITableView 单元格图像不持久

    我正在使用 SDWebImage 库来从 Web 服务异步下载和缓存图像 以下是我用来下载图像的方法 void downloadThumbnails NSURL finalUrl SDWebImageManager manager SDWe
  • iOS SDWebImage 淡入新图像

    我一直在 iPhone 应用程序上使用 SDWebImage 来处理所有图像加载 我正在使用占位符图像 并且我想在新图像加载后交叉淡入淡出或淡入淡出 我正在使用成功块来设置图像 并且效果很好 无论我如何尝试 图像都不会消失 我尝试将动画代码
  • SDWebImage UIImageView+WebCache 在 64 位设备上崩溃

    我有这个代码 import
  • 获取 SDWebImage 缓存图像

    我想问一下SDWebImageManager下载后如何获取下载的图像 我只有通过 URL 下载它的代码 这就是我得到的 let manager SDWebImageManager SDWebImageManager sharedManage

随机推荐

  • webgl 第三人称相机

    发现自己越来越懒了 基础的我不写了 有很多博客都写了 连w3c都有 瞬间没有心思写下去了 并且也很忙 每天都在研究 别骂我 真心没心思 我会考虑写一些难度大点的 这样有动力点 代码如下 看下面需要注意的是向量的计算
  • 计算机桌面背景显示方式有几种,如何让计算机自动更改桌面背景

    您可能对以下主题感兴趣 如何使计算机自动更改桌面背景 核心提示 本教程将向您展示如何使计算机自动更改桌面背景 Windows 7支持多种桌面背景切换功能 以使我们的桌面更加美观 但对于喜欢按文件夹保存图片的用户 如果在设置桌面背景时指定了包
  • 测试开发必备技能与成长路径

    可以通过学习不断往上攻关 比如搭建测试平台 掌握代码分析能力 进行持续交付 代码质量监控等
  • Vue Router 的query和params的区别?

    区别一 1 query相当于get请求 页面跳转的时候可以在地址栏看到请求参数 2 params相当于post请求 参数不会在地址栏中显示 所以用params传值相对安全 简记 params参数在地址栏中不会显示 query会显示 区别二
  • Ubuntu18.04.2设置系统时间问题

    Ubuntu18 04 2设置系统时区问题 出现情况 系统时区设置正确 但是系统时间比实际快8小时 原因为系统硬件时间是UTC格式 需要将其改为CST格式 使用以下命令 timedatectl set local rtc adjust sy
  • 功耗测评

    STM32F103C8T6 MCU越来越广泛的应用在生产生活的各个领域 外接丰富的传感器 功能模块 通信模块 显示存储等可以形成各种可样的产品项目应用 对于功耗要求比较高的产品 一般会选择STM32L系列的MCU 但是从功耗的评测角度 逻辑
  • 内容产品项目作品

    一 项目背景介绍 如果让大家来做一个没接触过的产品 思路 先了解产品所在的行业 多看看第三方的行业研究的文章 了解产品的主要竞品 这样做产品设计的时候不会乱搞 常见的内容产品形态 图文 音视频 直播等 二 内容产品生态及产品架构 产品架构端
  • < ElementUi组件库: el-progress 进度条Bug及样式调整 >

    ElementUi组件库 el progress 进度条Bug及样式调整 前言 一 实现原理 gt 修改 el progress 进度条样式 及 渐变进度条样式 二 案例代码 前言效果图案例 gt HTML代码 gt CSS代码 三 效果演
  • Unity使用vscode无法跳转到引用等一些问题

    软件 unity2019 vscode 前提 一直在用visiostudio做Unity开发 但vscode确实有很多优点 今天在一个全新电脑下搭建环境 竟遇到了许多问题 vscode无法跳转到引用 F12不起作用 无法引用using Un
  • V字钩爪。

    题目链接 题解 分块 就是有一些点之间是永远没有交集的 因为每一个点只与前面第k个元素以及后面那k个元素有关 所以我们应该把那些出现矛盾的点放到一起 然后进行讨论 下面是AC代码 include
  • java包的介绍

    包 包就是文件夹 用来管理各种不同功能的 java 类 方便后续管理 包名的规则 公司域名反写 包的作用 需要全部英文小写 见名知意 导包的规则 使用同一个包中的类时 不需要导包 使用 java Lang 包中的类时 不需要导包 其他情况都
  • python中怎么样统计一张图像中白色和黑色像素点个数(二值图)

    前言 有时候需要计算一张图片里的里黑白像素的个数来计算某些指标 所以这里就教大家怎么计算 代码实现 import numpy as np import cv2 二值化图像直接 print len im im 255 print len im
  • centos6、centos7、ubntu去除ip link 中bond

    说明 在对服务器重新配置网络时候 往往需要提前清楚网络和网卡相关信息 这里坐下说明和记录 centos6去除网络配置 网络配置路径 etc sysconfig network scripts 去除网络配置前先做备份 mkdir opt ne
  • SpringMVC实现文件的上传和下载

    SpringMVC文件的上传和下载 一 SpringMVC文件上传 1 什么是文件上传 文件上传 指的就是将用户本地计算机中文件上传到服务器上的过程称之为文件上传 2 文件上传编程步骤 1 Client选择文件 上传页面 1 method
  • TypeScript的基础类型

    1 boolean类型 boolean表示布尔值类型 即true和false 例 let isDone boolean false 2 number类型 number表示数字类型 包括整数和浮点数 例 let decimal number
  • 如何开发和部署微信小程序

    今天开始 用ChatGPT创作技术内容了 要开发和部署微信小程序 您需要遵循以下步骤 1 注册微信小程序账号 在微信公众平台注册小程序账号 通过审核后即可开始开发 2 下载开发工具 微信小程序开发工具是一款免费的IDE 可在Windows
  • ElasticSearch7 数据类型之日期类型

    日期类型 Date datatype Elasticsearch7 日期表达成类型 日期格式化的字符串 比如 2015 01 01 或者 2015 01 01 12 10 30 毫秒级别的 long 类型 秒级别的 integer 类型 M
  • 基础实验篇

    导读 uORB是PX4 Pixhawk系统中非常重要且关键的模块之一 是用于无人机模块间通信的协议机制 本篇将详细介绍uORB并详细拆解uORB消息读写与自定义实验全流程 二 基础实验篇 uORB消息读写与自定义实验 二 01 RflySi
  • hive在执行任务的时候提示java.io.FileNotFoundException: File does not exist: hdfs://Master:9000/usr/local/apach

    1 错误信息 Number of reduce tasks determined at compile time 1 In order to change the average load for a reducer in bytes se
  • iOS架构师_SDWebImage源码分析

    1 对SDdemo的分析 针对SD的3 8 2版本进行分析 因为从git上面直接下载SD的源码 项目运行会报错 缺少libwebp这个库 我们下载3 8 2版本 运行 libwebp 是个webp的图片格式 属于谷歌的库 所以只能翻墙去国外