仿SDWebImage
仿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. NSURL
NSURL *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的更多相关文章
- iOS学习路线图
一.iOS学习路线图 二.iOS学习路线图--视频篇 阶 段 学完后目标 知识点 配套学习资源(笔记+源码+PPT) 密码 基础阶段 学习周期:24天 学习后目标: ...
- iOS - ImageCache 网络图片缓存
1.ImageCache 使用内存缓存方式: 使用沙盒缓存方式: 使用网络图片第三方库方式: SDWebImage: iOS 中著名的网络图片处理框架 包含的功能:图片下载.图片缓存.下载进度监听.g ...
- iOS-----GitHub上比较齐全的iOS 工具和App
Github-iOS 工具 和 App 系统基础库 Category/Util sstoolkit 一套Category类型的库,附带很多自定义控件 功能不错- BFKit 又一套Ca ...
- 总结SUMMARY
Summary 多线程 多线程 pthread NSThread 创建线程的方式 NSThread 的 Target 线程状态 线程属性 资源共享 原子属性 线程间通讯 GCD 同步 & 异步 ...
- IOS 使用SDWebImage实现仿新浪微博照片浏览器
使用第三方库SDWebImage实现仿新浪微博照片浏览器,可以下载图片缓存,点击之后滚动查看相片,具体效果如下: 代码如下: WeiboImageView.h: #import <UIKit/U ...
- OC高仿iOS网易云音乐AFNetworking+SDWebImage+MJRefresh+MVC+MVVM
效果 因为OC版本大部分截图和Swift版本一样,所以就不再另外截图了. 列文章目录 因为目录比较多,每次更新这里比较麻烦,所以推荐点击到主页,然后查看iOS云音乐专栏. 目简介 这是一个使用OC语言 ...
- 高仿一元云购IOS应用源码项目
高仿一元云购IOS应用(高仿自一元云购安卓客户端) 本App因官方没有IOS客户端故开发,利用业务时间历时2个星期,终于开发完成,又因苹果的各大审核规则对此App的影响,又历时1个多月才终于成功上架, ...
- iOS高仿app源码:纯代码打造高仿优质《内涵段子》
iOS高仿app源码:纯代码打造高仿优质<内涵段子>收藏下来 字数1950 阅读4999 评论173 喜欢133 Github 地址 https://github.com/Charlesy ...
- iOS 高仿:花田小憩3.0.1
前言 断断续续的已经学习Swift一年多了, 从1.2到现在的2.2, 一直在语法之间徘徊, 学一段时间, 工作一忙, 再捡起来隔段时间又忘了.思来想去, 趁着这两个月加班不是特别多, 就决定用swi ...
随机推荐
- <路径算法>哈密顿路径变种问题(2016华为软件精英挑战赛初赛)
原创博客,转载请联系博主! 前言:几天前华为的这个软件精英(算法外包)挑战赛初赛刚刚落幕,其实这次是我第二次参加,只不过去年只入围到了64强(32强是复赛线),最后搞到了一个华为的一顶帽子(感谢交大l ...
- JAVA编写WEB服务器
一.超文本传输协议 1.1 HTTP请求 1.2 HTTP应答 二.Socket类 三.ServerSocket类 四.Web服务器实例 4.1 HttpServer类 4.2 Requ ...
- c#中如何做日期的三元判断(日期不为空赋值)
<dx:ASPxDateEdit runat="server" ID="edTab4_protocoldate" Width="100%&quo ...
- ASP.NET MVC4 学习系统四(视图)
视图(Views) 在ASP.NET MVC框架中,想要返回给用户HTML的控制器操作,就要返回ActionResult类型的ViewResult实例,ActionResult知道如何渲染应答结 ...
- .net验证码生成及使用
验证码的作用: 几年前,大部分网站.论坛之类的是没有验证码的,因为对于一般用户来说验证码只是增加了用户的操作,降低了用户的体验.但是后来各种灌水机器人.投票机器人.恶意注册机器人层出不穷,大大增加了网 ...
- zedboard如何从PL端控制DDR读写(三)——AXI-FULL总线调试
之前的项目和培训中,都只用到了AXI-Lite或者AXI-Stream,对于AXI-FULL知之甚少,主要是每次一看到那么多接口信号就望而却步了. 现在为了调试DDR,痛下决心要把AXI-FULL弄懂 ...
- 从Git仓库中恢复已删除的分支、文件或丢失的commit
亲测可用 因为自己 commit 并且 push 后 因为冲突 提交不了,不小心做了 rebase 代码被 覆盖 用以下命令 还原: 查看所有日志 并记下 hash 值 git reflog 然后用: ...
- Save a bricked Samsung Note 3 and do extraction
The case scenario was about bank robery and the suspect threw his Samsung Note 3 into the river. For ...
- Android IOS WebRTC 音视频开发总结(三二)-- WebRTC项目开发建议
本文主要介绍WEBRTC开发过程中的一些现象,文章来自博客园RTC.Blacker,支持原创,欢迎关注微信公众号blacker,更多详见www.rtc.help 随着移动互联网和智能硬件的快速发展,音 ...
- 十四、Struts2的国际化
十四.Struts2的国际化 1.配置全局国际化消息资源包 配置全局消息资源包 <!--配置全局消息资源包 --> <constant name="struts.c ...