仿SDWebImage

目标:模拟 SDWebImage 的实现

说明:整体代码与之前博客上的演练代码的基本一致,只是编写顺序会有变化!

在模仿 SDWebImage 之前,首先需要补充一个知识点:NSOperation自定义操作

下载操作实现

#import "NSString+Path.h"@interface DownloadImageOperation()
/// 要下载图像的 URL 字符串
@property (nonatomic, copy) NSString *URLString;
/// 完成回调 Block
@property (nonatomic, copy) void (^finishedBlock)(UIImage *image);
@end@implementation DownloadImageOperation+ (instancetype)downloadImageOperationWithURLString:(NSString *)URLString finished:(void (^)(UIImage *))finished {DownloadImageOperation *op = [[DownloadImageOperation alloc] init];op.URLString = URLString;op.finishedBlock = finished;return op;
}- (void)main {@autoreleasepool {// 利用断言要求必须传入完成回调,简化后续代码的分支NSAssert(self.finishedBlock != nil, @"必须传入回调 Block");// 1. NSURLNSURL *url = [NSURL URLWithString:self.URLString];// 2. 获取二进制数据NSData *data = [NSData dataWithContentsOfURL:url];// 3. 保存至沙盒if (data != nil) {[data writeToFile:self.URLString.appendCachePath atomically:YES];}if (self.isCancelled) {NSLog(@"下载操作被取消");return;}// 主线程回调[[NSOperationQueue mainQueue] addOperationWithBlock:^{self.finishedBlock([UIImage imageWithData:data]);}];}
}

断言

  • 断言是所有 C 语言开发者的最爱
  • 断言能够在程序编码时提前预判必须满足某一个条件
  • 如果条件不满足,直接让程序崩溃,从而让程序员尽早发现错误
  • 断言仅在调试时有效
  • 断言可以简化程序的分支逻辑

测试下载操作

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {int seed = arc4random_uniform((UInt32)self.appList.count);AppInfo *app = self.appList[seed];// 取消之前的下载操作if (![app.icon isEqualToString:self.currentURLString]) {// 取消之前操作[self.operationCache[self.currentURLString] cancel];}// 记录当前操作self.currentURLString = app.icon;// 创建下载操作DownloadImageOperation *op = [DownloadImageOperation downloadImageOperationWithURLString:app.icon finished:^(UIImage *image) {self.iconView.image = image;// 从缓冲池删除操作[self.operationCache removeObjectForKey:app.icon];}];// 将操作添加到缓冲池[self.operationCache setObject:op forKey:app.icon];// 将操作添加到队列[self.downloadQueue addOperation:op];
}

框架结构设计

下载管理器

  • 单例实现
+ (instancetype)sharedManager {static id instance;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{instance = [[self alloc] init];});return instance;
}

之所以设计成单例,是为了实现全局的图像下载管理

  • 移植属性和懒加载代码
/// 下载队列
@property (nonatomic, strong) NSOperationQueue *downloadQueue;
/// 下载操作缓存
@property (nonatomic, strong) NSMutableDictionary *operationCache;// MARK: - 懒加载
- (NSMutableDictionary *)operationCache {if (_operationCache == nil) {_operationCache = [NSMutableDictionary dictionary];}return _operationCache;
}- (NSOperationQueue *)downloadQueue {if (_downloadQueue == nil) {_downloadQueue = [[NSOperationQueue alloc] init];}return _downloadQueue;
}
  • 定义方法
///  下载指定 URL 的图像
///
///  @param URLString 图像 URL 字符串
///  @param finished  下载完成回调
- (void)downloadImageOperationWithURLString:(NSString *)URLString finished:(void (^)(UIImage *image))finished;
  • 方法实现
- (void)downloadImageOperationWithURLString:(NSString *)URLString finished:(void (^)(UIImage *))finished {// 检查操作缓冲池if (self.operationCache[URLString] != nil) {NSLog(@"正在玩命下载中,稍安勿躁");return;}// 创建下载操作DownloadImageOperation *op = [DownloadImageOperation downloadImageOperationWithURLString:URLString finished:^(UIImage *image) {// 从缓冲池删除操作[self.operationCache removeObjectForKey:URLString];// 执行回调finished(image);}];// 将操作添加到缓冲池[self.operationCache setObject:op forKey:URLString];// 将操作添加到队列[self.downloadQueue addOperation:op];
}

修改 ViewController 中的代码

  • 删除相关属性和懒加载方法
  • 用下载管理器接管之前的下载方法
// 创建下载操作
[[DownloadImageManager sharedManager] downloadImageOperationWithURLString:self.currentURLString finished:^(UIImage *image) {self.iconView.image = image;
}];
  • 增加取消下载功能
///  取消指定 URL 的下载操作
- (void)cancelDownloadWithURLString:(NSString *)URLString {// 1. 从缓冲池中取出下载操作DownloadImageOperation *op = self.operationCache[URLString];if (op == nil) {return;}// 2. 如果有取消[op cancel];// 3. 从缓冲池中删除下载操作[self.operationCache removeObjectForKey:URLString];
}

运行测试!

缓存管理

  • 定义图像缓存属性
/// 图像缓存
@property (nonatomic, strong) NSMutableDictionary *imageCache;
  • 懒加载
- (NSMutableDictionary *)imageCache {if (_imageCache == nil) {_imageCache = [NSMutableDictionary dictionary];}return _imageCache;
}
  • 检测图像缓存方法准备
///  检查图像缓存
///
///  @return 是否存在图像缓存
- (BOOL)chechImageCache {return NO;
}
  • 方法调用
// 如果存在图像缓存,直接回调
if ([self chechImageCache]) {finished(self.imageCache[URLString]);return;
}
  • 缓存方法实现
- (BOOL)chechImageCache:(NSString *)URLString {// 1. 如果存在内存缓存,直接返回if (self.imageCache[URLString]) {NSLog(@"内存缓存");return YES;}// 2. 如果存在磁盘缓存UIImage *image = [UIImage imageWithContentsOfFile:URLString.appendCachePath];if (image != nil) {// 2.1 加载图像并设置内存缓存NSLog(@"从沙盒缓存");[self.imageCache setObject:image forKey:URLString];// 2.2 返回return YES;}return NO;
}

运行测试

自定义 UIImageView

  • 目标:

    • 利用下载管理器获取指定 URLString 的图像,完成后设置 image
    • 如果之前存在未完成的下载,判断是否与给定的 URLString 一致
    • 如果一致,等待下载结束
    • 如果不一致,取消之前的下载操作
  • 定义方法

///  设置指定 URL 字符串的网络图像
///
///  @param URLString 网络图像 URL 字符串
- (void)setImageWithURLString:(NSString *)URLString;
  • 方法实现
@interface WebImageView()
///  当前正在下载的 URL 字符串
@property (nonatomic, copy) NSString *currentURLString;
@end@implementation WebImageView- (void)setImageWithURLString:(NSString *)URLString {// 取消之前的下载操作if (![URLString isEqualToString:self.currentURLString]) {// 取消之前操作[[DownloadImageManager sharedManager] cancelDownloadWithURLString:self.currentURLString];}// 记录当前操作self.currentURLString = URLString;// 创建下载操作__weak typeof(self) weakSelf = self;[[DownloadImageManager sharedManager] downloadImageOperationWithURLString:URLString finished:^(UIImage *image) {weakSelf.image = image;}];
}@end
  • 修改 ViewController 中的调用代码
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {int seed = arc4random_uniform((UInt32)self.appList.count);AppInfo *app = self.appList[seed];[self.iconView setImageWithURLString:app.icon];
}

运行时机制 —— 关联对象

// MARK: - 运行时关联对象
const void *HMCurrentURLStringKey = "HMCurrentURLStringKey";- (void)setCurrentURLString:(NSString *)currentURLString {objc_setAssociatedObject(self, HMCurrentURLStringKey, currentURLString, OBJC_ASSOCIATION_COPY_NONATOMIC);
}- (NSString *)currentURLString {return objc_getAssociatedObject(self, HMCurrentURLStringKey);
}
  • 为了防止 Cell 重用,取消之前下载操作的同时,清空 image
self.image = nil;

SDWebImage常见面试题

1> 图片文件缓存的时间有多长:1周

_maxCacheAge = kDefaultCacheMaxCacheAge

2> SDWebImage 的内存缓存是用什么实现的?

NSCache

3> SDWebImage 的最大并发数是多少?

maxConcurrentDownloads = 6
* 是程序固定死了,可以通过属性进行调整!

4> SDWebImage 支持动图吗?GIF

#import <ImageIO/ImageIO.h>
[UIImage animatedImageWithImages:images duration:duration];

5> SDWebImage是如何区分不同格式的图像的

  • 根据图像数据第一个字节来判断的!

    • PNG:压缩比没有JPG高,但是无损压缩,解压缩性能高,苹果推荐的图像格式!
    • JPG:压缩比最高的一种图片格式,有损压缩!最多使用的场景,照相机!解压缩的性能不好!
    • GIF:序列桢动图,特点:只支持256种颜色!最流行的时候在1998~1999,有专利的!

6> SDWebImage 缓存图片的名称是怎么确定的!

  • md5

    • 如果单纯使用 文件名保存,重名的几率很高!
    • 使用 MD5 的散列函数!对完整的 URL 进行 md5,结果是一个 32 个字符长度的字符串!

7> SDWebImage 的内存警告是如何处理的!

  • 利用通知中心观察
  • - UIApplicationDidReceiveMemoryWarningNotification 接收到内存警告的通知
    • 执行 clearMemory 方法,清理内存缓存!
  • - UIApplicationWillTerminateNotification 接收到应用程序将要终止通知
    • 执行 cleanDisk 方法,清理磁盘缓存!
  • - UIApplicationDidEnterBackgroundNotification 接收到应用程序进入后台通知
    • 执行 backgroundCleanDisk 方法,后台清理磁盘!
    • 通过以上通知监听,能够保证缓存文件的大小始终在控制范围之内!
    • clearDisk 清空磁盘缓存,将所有缓存目录中的文件,全部删除!
      实际工作,将缓存目录直接删除,再次创建一个同名空目录!

仿SDWebImage相关推荐

  1. 图片懒加载(仿SDWebImage)

    1.图片缓存 #import "UIImageView+WebCache.h" #import "ImageDownloader.h"@implementati ...

  2. 史上最全的iOS开源项目分类汇总

    学了这么久,还是抽时间把github上比较好用的第三方总结了一下: Category/Util sstoolkit 一套Category类型的库,附带很多自定义控件 功能不错- BFKit 又一套Ca ...

  3. github上比较好用的第三方

    本片博文转于:AirZilong的博客(个人感觉很不错,感谢 AirZilong的分享) 学了这么久,还是抽时间把github上比较好用的第三方总结了一下: Category/Util  sstool ...

  4. iOS-----GitHub上比较齐全的iOS 工具和App

    Github-iOS 工具 和 App 系统基础库 Category/Util  sstoolkit 一套Category类型的库,附带很多自定义控件 功能不错-        BFKit 又一套Ca ...

  5. 【图文详解】一文全面彻底搞懂HBase、LevelDB、RocksDB等NoSQL背后的存储原理:LSM-tree 日志结构合并树...

    LSM 树广泛用于数据存储,例如 RocksDB.Apache AsterixDB.Bigtable.HBase.LevelDB.Apache Accumulo.SQLite4.Tarantool.W ...

  6. 全部第三方工具简介 和网址 下载

    常见动画 全部展开  全部折叠 系统基础库网址    http://github.ibireme.com/github/list/ios/ DSA /Util 基础功能  objection 一个估计 ...

  7. iOS进阶之架构设计MVVM模式仿新闻项目(6)

    这是MVVM的第三篇文章了,之所以花这么多文章来介绍MVVM,就是为了加深对MVVM的理解,以及从不同demo的角度,对比分析那种是最适合自己的模式. 转自文章 iOS使用MVVM模式仿新闻项目 一. ...

  8. 【无限互联】学员作品:仿汽车之家的App

    一.项目介绍:    本次项目是一个仿汽车之家的一款App:它主要是用来帮助人们不用出门就可以学习和掌握关于汽车各种知识和信息,也可以帮想买车的朋友买到一款符合你各种需求的好车. 二.效果图: 1.首 ...

  9. 精仿百思不得姐客户端应用iOS源码

    XFBaiSiBuDeJie 高仿百思不得姐客户端 初次学习使用RAC,还不是怎么熟悉,使用的仍是MVC模式,MVVM还在摸索中... 如果大家觉得还不错,请给颗星星支持下~~~ 程序中使用到的库 A ...

最新文章

  1. 【Python】 linecache模块读取文件
  2. IBM 推出 Bluemix :Swift 将支持服务器端开发
  3. python控制鼠标点击标准模块_Python直接控制鼠标键盘模块 pyautogui
  4. java 反向映射,如何使用lambdas實現反向映射Java?
  5. [渝粤教育] 广东-国家-开放大学21秋期末考试中国近现代史纲要(A)10881k1
  6. matlab小波脊线,小波脊线提取,模极大值法。运行的结果不太对,代码有些地方我也没完全看懂...
  7. OpenStack-Zun 使用
  8. java 面试题解惑一 类的初始化顺序
  9. 五分钟用Docker快速搭建Go开发环境
  10. html的音频播放,HTML5 音频播放 audio
  11. 儿童机器人编程语言_儿童编程机器人
  12. XShell安装配置教程
  13. 狄拉克量子力学原理【1】态叠加原理
  14. 经典枚举——百钱百鸡问题
  15. ESN学习笔记——原理与超参数
  16. 华为全连MGRE与星型拓扑MGRE(全网状与非全网状)
  17. 自动化测试工具 Selenium WebDriver 入门教程
  18. python/appium实现华为应用商城app界面上下滑动打开关闭通知栏等功能
  19. Win7电脑右下角声音图标小喇叭出现红叉没有声音解决方案
  20. 服务器中搭建OA系统,oa系统搭建在云服务器上

热门文章

  1. php表格设置标题,每页表格标题字PHPWord
  2. 彻底解决Word中子标题变黑块问题
  3. java webservice开发和调用(jdk1.5+eclipse3.4 + tomcat5.5+axis1.4+xfire1.2.6)
  4. fedora安装fcitx
  5. JAVA核心知识点--HttpClient获取302响应中的Location头信息
  6. c语言程序中计算圆的面积,C代码:使用概率的方法计算圆的面积
  7. 腾讯AI Lab 提出「完全依存森林」,大幅缓解关系抽取中的错误传递
  8. 已知鸡和兔的总数量n,总脚数为m。输入n和m,依次输出鸡和兔的数目。如果无解,输出“no answer”。 将下面的代码填写完整。
  9. HTML+CSS+JS家乡主题网页设计 学生网页设计作品 dreamweaver作业静态HTML网页设计模板 旅游景点网页作业制作
  10. 高等数学(第七版)同济大学 总习题九(后10题) 个人解答