重点: 1.分析项目需求; 2.处理内存警告,优化用户体验; 3.技术选择,技术点实施,代码编写. 4.Bug?
{
项目需求:
<1> 下载图片;
分析:
1>.子线程下载图片,主线程显示图片.
2>.开启子线程有三种技术方案可供选择: (1)NSThread ,(2)GCD ,(3)NSOperation 配合 NSOperationQueue使用.
<2> 内存警告处理;
分析:
接收到内存警告的时候,停止一切下载操作,防止闪退.
<3> 用户体验;
分析:
在与用户做 UI 交互的时候,最好暂停图片的下载;用户滚动结束之后,再继续下载图片.
1. 技术选择 : (3)NSOperation 配合 NSOperationQueue使用.
2. 技术点实施:
// <1>用户开始滚动的时候,暂停下载操作;停止滚动之后,恢复下载操作.
{
#pragma UIScrollViewDelegate
// 开始滚动的时候调用
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
NSLog(@"暂停下载---");
// 暂停所有下载操作
[self.queue setSuspended:YES];
}
// 滚动结束的时候调用
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
NSLog(@"恢复下载---");
// 恢复所有下载操作
[self.queue setSuspended:NO];
}
}
// <2>接收到内存警告的时候,取消一切操作.
{
// 接收到内存警告的时候调用
-(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// 取消一切下载操作
[self.queue cancelAllOperations];
}
}
// <3>将下载图片的操作封装在 NSBlockOperation.最后将操作放在并发队列中.自动执行!
{
__weak typeof(self) wself = self;
// 定义下载操作
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// 下载网络图片
UIImage *webImage = [wself downloadWebImage:app.icon];
//回到主线程显示图片
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// 显示图片
cell.imageView.image = webImage;
}];
}];
// 将下载操作放在并发队列中.自动开启下载.
[self.queue addOperation:op];
}
3.运行程序之后,发现 3 个Bug:
<1> 程序运行之后,图片不会直接显示出来.需要刷新之后才能显示(滚动/点击都会重绘 UI).
<2> 图片错位问题.
<3> 用户体验方面的Bug:只要滚动,图片就会重复下载.即使已经下载好的图片,也会重新下载.(耗费流量,体验巨差,巨耗电).
}
发现 3 个Bug解决方案:
1.分析Bug产生的原因并解决Bug. 2.如何防止一个url对应的图片重复下载?
{
Bug 产生的原因分析:
<1> "程序运行之后,图片不会直接显示出来.需要刷新之后才能显示(滚动/点击都会重绘 UI)."
Bug产生原因: UITableViewCell 中刚开始没有设置图片的 Frame,图片下载完后,点击/刷新之后,就会重新绘制 UITableViewCell,这样就会显示图片了.
解决 Bug :下载之前最好先把图片的 Frame 绘制出来.比如,可以添加一张占位图片.
{
// 设置占位图片.
cell.imageView.image = [UIImage imageNamed:@"placeholder"];
}
<2> "图片错位问题."
Bug产生原因: UITableViewCell 的重用以及网络下载延时产生的.
解决 Bug :让数据控制视图(视图根据数据来显示),设置一个图片缓存,cell 中的图片来源于这个图片缓存.
{
<1>.设置图片缓存.
定义一个字典做为图片缓存,保存下载好的图片(以图片的 url 作为 key 值;以下载好的图片为 Value).
// 可以选择 NSCache 代替字典作为缓存机制.
// NSCache在内存紧张的时候,会自动释放一些资源(自动销毁图片,我们无法控制).
// 如果使用字典,在接收到内存警告之后,需要手动释放资源.
<2>.从缓存中取出 cell 对应的图片.
cell 设置图片:根据 cell 的 从字典中取出对应的图片.
<3>.图片下载完毕之后刷新所在行的数据.
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
注意: 没必要刷新整个表格视图,刷新所在行的数据就可以了.
}
<3> "用户体验方面的Bug:只要滚动,图片就会重复下载.即使已经下载好的图片,也会重新下载.(耗费流量,体验巨差,巨耗电)."
Bug产生原因: 只要上下滚动,就会调用 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 这个方法.这个方法频繁调用导致下载操作一直创建.
解决 Bug :每次创建下载操作之前,最好先检查一下载操作是否存在,对于已经存在的操作,不要重复下载.
{
<1>.设置操作缓存.
定义一个字典作为下载操作缓存,保存已经创建好的下载操作(同样,以图片的 url 为 key值;以操作Operation为 Value).
<2>.从缓存中取出操作比较.
每次重新创建下载操作之前,首先从下载操作缓存之中查看操作是否已经存在.如果存在,就不要再次创建;如果不存在,创建新的下载操作.
<3>.防止操作缓存越来越大
图片下载成功之后,就下载操作就没有存在的意义了,应该及时清除缓存,防止下载操作缓存越来越大.
}