一、SDWebImage介绍
- 什么是SDWebImage
- iOS中著名的牛逼的网络图片处理框架
- 包含的功能:图片下载、图片缓存、下载进度监听、gif处理等等
- 用法极其简单,功能十分强大,大大提高了网络图片的处理效率
- 国内超过90%的iOS项目都有它的影子
- 项目地址
- https://github.com/rs/SDWebImage
- 下载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 创建可重用的cellstatic NSString *reuseId = @"appInfo";HMAppInfoCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseId];//2 获取数据,给cell内部子控件赋值HMAppInfo *appInfo = self.appInfos[indexPath.row]; cell.appInfo = appInfo;//3 返回cellreturn 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) {//图片下载完成更新UINSLog(@"更新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 weekb. 使用的缓存对象_memCache = [[NSCache alloc] init];c. SDImageCache内处理内存警告,以通知的方式,clearMemoryd. cleanDisk的执行过程
i. 先遍历所有的缓存文件,记录过期的文件,计算缓存文件的总大小
ii. 删除过期的文件
iii. 判断maxCacheSize的值是否>0,如果大于0再判断缓存的文件总大小是否大于maxCacheSize
iv.如果缓存文件的总大小超过maxCacheSize,删除最早的文件1. SDWebImage的最大并发数是多少?
SDWebImageDownloader.m中的init方法
_downloadQueue.maxConcurrentOperationCount = 6;2. SDWebImage支持gif么?
支持。UIImage+GIF3. 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