一、SDWebImage介绍
- 什么是SDWebImage
- iOS中著名的牛逼的网络图片处理框架
- 包含的功能:图片下载、图片缓存、下载进度监听、gif处理等等
- 用法极其简单,功能十分强大,大大提高了网络图片的处理效率
- 国内超过90%的iOS项目都有它的影子
- 项目地址
- 下载SDWebImage
- 查看文档,SDWebImage的基本使用
- 导入UIImageView+WebCache头文件
- 下载图片,并显示在imageView中
[self.imageView sd_setImageWithURL:url];
异步下载网络图片 使用SDWebImage
#import "ViewController.h"
#import "HMAppInfo.h"
#import "HMAppInfoCell.h"
#import "NSString+Sandbox.h"
@interface ViewController ()
@property (nonatomic, strong) NSArray *appInfos;
@end
@implementation ViewController
//懒加载
- (NSArray *)appInfos {
if (_appInfos == nil) {
_appInfos = [HMAppInfo appInfos];
}
return _appInfos;
}
//2 数据源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.appInfos.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//1 创建可重用的cell
static NSString *reuseId = @"appInfo";
HMAppInfoCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseId];
//2 获取数据,给cell内部子控件赋值
HMAppInfo *appInfo = self.appInfos[indexPath.row];
cell.appInfo = appInfo;
//3 返回cell
return cell;
}
@end
HMAppInfoCell.m 图片重命名是防止不同服务器过来的图片 重复 是通过md5值计算过的
#import "HMAppInfoCell.h"
#import "HMAppInfo.h"
#import "UIImageView+WebCache.h"
@interface HMAppInfoCell ()
@property (weak, nonatomic) IBOutlet UILabel *nameView;
@property (weak, nonatomic) IBOutlet UILabel *downloadView;
@property (weak, nonatomic) IBOutlet UIImageView *iconView;
@end
@implementation HMAppInfoCell
- (void)setAppInfo:(HMAppInfo *)appInfo {
_appInfo = appInfo;
self.nameView.text = appInfo.name;
self.downloadView.text = appInfo.download;
[self.iconView sd_setImageWithURL:[NSURL URLWithString:appInfo.icon]];
}
@end
二、自定义Operation
- 自定义Operation的目的:封装下载图片的操作
- 自定义Operation,模拟下载图片
- 创建HMDownloaderOperation类,继承自NSOperation
- 重写自定义Operation的main方法
- 重写- (void)main方法,在里面实现想执行的任务
- 自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)
- 经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应
- 在controller中调用start方法,或者添加到队列。main方法会被调用
自动释放池 1.循环内部创建了大量的临时对象 2.子线程无法访问主线程的自动释放池
传参和其他
- 自定义Operation传参
//要下载的图片的地址
@property (nonatomic, copy) NSString *urlString;
//回调
@property (nonatomic, copy) void (^finishedBlock)(UIImage *img);
断言的使用
NSAssert(self.finishedBlock != nil, @"finishedBlock 不能为nil”);
自定义Operation取消操作
if (self.isCancelled) {
return;
}
HMDownloaderOperation.h
#import <UIKit/UIKit.h>
//自定义操作
//1 创建一个类继承自NSOperation
//2 重写main方法,自动释放池
//3 定义属性接收参数 , 类方法快速初始化自定义操作
//4 取消操作(取消正在执行的操作)
//5 断言NSAssert
@interface HMDownloaderOperation : NSOperation
//要下载图片的地址
@property (nonatomic, copy) NSString *urlString;
//执行完成后,回调的block
@property (nonatomic, copy) void (^finishedBlock)(UIImage *img);
+ (instancetype)downloaderOperationWithURLString:(NSString *)urlString finishedBlock:(void(^)(UIImage *img))finishedBlock;
@end
HMDownloaderOperation.m
#import "HMDownloaderOperation.h"
@implementation HMDownloaderOperation
+ (instancetype)downloaderOperationWithURLString:(NSString *)urlString finishedBlock:(void (^)(UIImage *))finishedBlock {
HMDownloaderOperation *op = [[HMDownloaderOperation alloc] init];
op.urlString = urlString;
op.finishedBlock = finishedBlock;
return op;
}
- (void)main {
@autoreleasepool {
//断言
NSAssert(self.finishedBlock != nil, @"finishedBlock 不能为nil");
//模拟网络延时
[NSThread sleepForTimeInterval:2.0];
//判断是否被取消 取消正在执行的操作
if (self.isCancelled) {
return;
}
NSLog(@"下载图片 %@ %@",self.urlString,[NSThread currentThread]);
//UIImage
//假设图片下载完成
//回到主线程更新UI
// if (self.finishedBlock) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.finishedBlock(self.urlString);
}];
// }
}
}
@end
ViewController.m
#import "ViewController.h"
#import "HMDownloaderOperation.h"
@interface ViewController ()
//全局队列
@property (nonatomic, strong) NSOperationQueue *queue;
@end
@implementation ViewController
//懒加载
- (NSOperationQueue *)queue {
if (_queue == nil) {
_queue = [[NSOperationQueue alloc] init];
_queue.maxConcurrentOperationCount = 2;
}
return _queue;
}
- (void)viewDidLoad {
[super viewDidLoad];
for (int i = 0; i<20; i++) {
HMDownloaderOperation *op = [HMDownloaderOperation downloaderOperationWithURLString:@"abc.jpg" finishedBlock:^(UIImage *img) {
//图片下载完成更新UI
NSLog(@"更新UI %d %@",i,[NSThread currentThread]);
}];
[self.queue addOperation:op];
}
//演示断言
// HMDownloaderOperation *op = [HMDownloaderOperation downloaderOperationWithURLString:@"abc.jpg" finishedBlock:nil];
// [self.queue addOperation:op];
// //自定义操作
// HMDownloaderOperation *op = [[HMDownloaderOperation alloc] init];
// op.urlString = @"xxx.jpg";
// //无法传递参数
[op setCompletionBlock:^{
NSLog(@"给控件赋值");
}];
// [op setFinishedBlock:^(UIImage *img) {
// NSLog(@"给控件赋值 %@",img);
// }];
// [self.queue addOperation:op];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//设置所有操作的canceled属性为yes
[self.queue cancelAllOperations];
NSLog(@"取消");
}
下载网络图片
//断言的使用
NSAssert(self.finishedBlock != nil, @"finishedBlock 不能为nil");
//下载网络图片
NSURL *url = [NSURL URLWithString:self.urlString];
NSData *data = [NSData dataWithContentsOfURL:url];
[NSThread sleepForTimeInterval:1.0];
//保存到沙盒
if (data) {
[data writeToFile:[self.urlString appendCachePath] atomically:YES];
}
//取消操作
if (self.isCancelled) {
return;
}
//主线程更新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
UIImage *img = [UIImage imageWithData:data];
self.finishedBlock(img);
}];
测试下载图片
controller中测试//获取随机的数字
int random = arc4random_uniform((u_int32_t)self.appInfos.count);
//随机获取模型
HMAppInfo *appInfo = self.appInfos[random];
//判断当前要下载的图片,是不是刚刚下载过的
if (![appInfo.icon isEqualToString:self.currentURLString]) {
//取消操作
[self.operationCache[self.currentURLString] cancel];
}
//记录当前显示的图片地址
self.currentURLString = appInfo.icon;
//下载并设置图片
HMDownloaderOperation *op = [HMDownloaderOperation downloaderOperationWithURLString:appInfo.icon finishedBlock:^(UIImage *img) {
self.imageview.image = img;
//移除下载操作
[self.operationCache removeObjectForKey:appInfo.icon];
}];
[self.queue addOperation:op];
//缓存当前下载操作
[self.operationCache setObject:op forKey:appInfo.icon];
仿SDWebImage
下载操作管理类
- 作用
- 管理全局的下载操作
- 管理全局的图片缓存
- 单例方法
+ (instancetype)sharedManager {
static id instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
- 属性和懒加载
@property (nonatomic, strong) NSOperationQueue *queue;
//操作缓存池
@property (nonatomic, strong) NSMutableDictionary *operationCache;
//懒加载
- (NSOperationQueue *)queue {
if (_queue == nil) {
_queue = [NSOperationQueue new];
}
return _queue;
}
- (NSMutableDictionary *)operationCache {
if (_operationCache == nil) {
_operationCache = [NSMutableDictionary dictionary];
}
return _operationCache;
}
- 下载的方法
- (void)downloaderOperationWithURLString:(NSString *)urlString finishedBlock:(void (^)(UIImage *))finishedBlock {
//断言
NSAssert(finishedBlock != nil, @"finishedBlock不能为nil");
//如果下载操作已经存在,退出
if (self.operationCache[urlString]) {
return;
}
//下载并设置图片
HMDownloaderOperation *op = [HMDownloaderOperation downloaderOperationWithURLString:urlString finishedBlock:^(UIImage *img) {
//移除下载操作
[self.operationCache removeObjectForKey:urlString];
//回调
finishedBlock(img);
}];
[self.queue addOperation:op];
//缓存当前下载操作
[self.operationCache setObject:op forKey:urlString];
}
- 取消的方法
- (void)cancelOperation:(NSString *)urlString {
if (urlString == nil) {
return;
}
//取消操作
[self.operationCache[urlString] cancel];
//从缓存池中移除
[self.operationCache removeObjectForKey:urlString];
}
- 缓存管理
//图片缓存
@property (nonatomic, strong) NSMutableDictionary *imageCache;
//下载图片之前,先检查图片缓存
if ([self checkImageCache:urlString]) {
finishedBlock(self.imageCache[urlString]);
return;
}
- (BOOL)checkImageCache:(NSString *)urlString {
//1 检查内存缓存
if (self.imageCache[urlString]) {
NSLog(@"内存缓存");
return YES;
}
//2 检查沙盒缓存
UIImage *img = [UIImage imageWithContentsOfFile:[urlString appendCachePath]];
if (img) {
NSLog(@"沙盒缓存");
//保存到内存缓存
[self.imageCache setObject:img forKey:urlString];
return YES;
}
return NO;
}
UIImageView的分类
- 模拟SDWebImage一行代码下载图片,创建UIImageView的分类
//记录当前显示的图片地址
@property (nonatomic, copy) NSString *currentURLString;
//设置图片的地址,下载图片
- (void)setImageUrlString:(NSString *)urlString {
//判断当前要下载的图片,是不是刚刚下载过的
if (![urlString isEqualToString:self.currentURLString]) {
//取消操作
[[HMDownloaderOperationManager sharedManager] cancelOperation:self.currentURLString];
}
//记录当前显示的图片地址
self.currentURLString = urlString;
//下载图片
[[HMDownloaderOperationManager sharedManager] downloaderOperationWithURLString:urlString finishedBlock:^(UIImage *img) {
self.image = img;
}];
}
问题
- 下载图片,崩溃
- 分类中不能直接添加输入,如果添加属性需要重写setter和getter方法。
关联对象
#import <objc/runtime.h>
#define HMMYKEY "str"
//关联对象,存储一个值
objc_setAssociatedObject(self, HMMYKEY, currentURLString, OBJC_ASSOCIATION_COPY_NONATOMIC);
//关联对象,取出值
return objc_getAssociatedObject(self, HMMYKEY);
第三方框架
- 什么是第三方框架
- 非官方制作的一些解决某一类的问题的框架(类库)
- 使用人数较多的框架,得到了充分的测试,bug几乎没有
- 一般开源
- 使用简单,方便
- 一般的使用步骤
- 看demo
- 看文档
- 通过文档试用,编译
- 实现自己项目中对应的功能
- 有空看源代码
NSCache
1. NSCache苹果提供的一套缓存机制
a. 和NSMutableDictionary使用起来相似
b. 线程安全,Mutable开发的类一般都是线程不安全的
c. 当内存不足时会自动释放内存(所以从缓存中取数据的时候总要判断是否为空)
d. 指定缓存的限额,当缓存超出限额自动释放内存
i. 缓存限额:
1) 缓存数量
@property NSUInteger countLimit;
2) 缓存成本
@property NSUInteger totalCostLimit;
2. 演示缓存的代理
//当缓存被移除的时候执行
- (void)cache:(NSCache *)cache willEvictObject:(id)obj{
NSLog(@"缓存移除 %@",obj);
}
常见面试题
a. 默认缓存的时间
_maxCacheAge = kDefaultCacheMaxCacheAge;
static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
b. 使用的缓存对象
_memCache = [[NSCache alloc] init];
c. SDImageCache内处理内存警告,以通知的方式,clearMemory
d. cleanDisk的执行过程
i. 先遍历所有的缓存文件,记录过期的文件,计算缓存文件的总大小
ii. 删除过期的文件
iii. 判断maxCacheSize的值是否>0,如果大于0再判断缓存的文件总大小是否大于maxCacheSize
iv.如果缓存文件的总大小超过maxCacheSize,删除最早的文件
1. SDWebImage的最大并发数是多少?
SDWebImageDownloader.m中的init方法
_downloadQueue.maxConcurrentOperationCount = 6;
2. SDWebImage支持gif么?
支持。UIImage+GIF
3. SDWebImage中怎么判断文件的类型的
NSData+ImageContentType.m中
根据文件头的第一个字节判断的
case 0xFF:
return @"image/jpeg";
case 0x89:
return @"image/png";
case 0x47:
return @"image/gif";
case 0x49:
case 0x4D:
return @"image/tiff";
4. SDWebImage缓存文件名称
为了防止缓存的图片名称冲突,根据md5计算的
md5重复的几率很小很小很小很小
终端中测试:echo -n "图片路径" | md5