问题1:如果网速比较慢,会很卡
运行结果如下:
解决方法:使用异步下载
问题2:图片没有Frame,所有cell初始化的时候,给imageView的frame是0。异步下载完之后不显示解决办法:使用占位图(如果展位图比较大,自定义cell可以解决)
问题3:如果图片下载速度不一致,同时用户快速滚动的时候,会因为Cell的重用导致图片混乱
解决方法:MVC,使用Model(模型)保存下载的图像,再次刷新表格。
问题4:在用户快速滚动的时候,会重复添加下载任务到下载队列。
解决方法:建立下载操作的缓冲池。首先检查缓冲池里是否有当前图片的下载操作。有的话就不创建下载操作。从而保证一张图片只添加一个下载操作。其实就是建立一个全局的字典属性。全局字典的键保存的是字符串类型的下载地址,而对应的对象是具体对应的操作。
原因如下:
解决重复添加操作问题的具体方案如下图:(添加操作缓存)
工程在上篇基础之上稍加修改:
CZApp.h
<span style="font-size:18px;">//
// CZApp.h
// NSoperation之网络图片下载
//
// Created by apple on 15/10/23.
// Copyright (c) 2015年 LiuXun. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface CZApp : NSObject
@property(nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *icon;
@property (nonatomic, strong) NSString *download;
/**
保存网络下载的图像
*/
@property(nonatomic, strong) UIImage *image;
+(instancetype) appWithDict:(NSDictionary *) dict;
@end
</span>
CZApp.m
<span style="font-size:18px;">//
// CZApp.m
// NSoperation之网络图片下载
//
// Created by apple on 15/10/23.
// Copyright (c) 2015年 LiuXun. All rights reserved.
//
#import "CZApp.h"
@implementation CZApp
+(instancetype) appWithDict:(NSDictionary *) dict
{
CZApp *app = [[self alloc] init];
[app setValuesForKeysWithDictionary:dict];
return app;
}
@end
</span>
控制器代码如下所示:
<span style="font-size:18px;">//
// ViewController.m
// NSoperation之网络图片下载
//
// Created by apple on 15/10/23.
// Copyright (c) 2015年 LiuXun. All rights reserved.
//
#import "ViewController.h"
#import "CZApp.h"
@interface ViewController ()
// plist文件数据的容器
@property (nonatomic, strong) NSArray *appList;
// 管理下载的全局队列
@property (nonatomic, strong) NSOperationQueue *opQueue;
/**所有下载的缓冲池*/
@property (nonatomic, strong) NSMutableDictionary *operationCache;
@end
@implementation ViewController
// 懒加载
-(NSArray *)appList
{
if (_appList == nil) {
NSArray *dicArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil]];
// 字典转模型
NSMutableArray *arryM = [NSMutableArray array];
for(NSDictionary *dict in dicArray){
CZApp *app = [CZApp appWithDict:dict];
[arryM addObject:app];
}
_appList = arryM;
}
return _appList;
}
-(NSOperationQueue *)opQueue
{
if (_opQueue == nil) {
_opQueue = [[NSOperationQueue alloc] init];
}
return _opQueue;
}
-(NSMutableDictionary *)operationCache
{
if (_operationCache == nil) {
_operationCache = [[NSMutableDictionary alloc] init];
}
return _operationCache;
}
#pragma mark - 实现数据源方法
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.appList.count;
}
/**
问题1:如果网速比较慢,会很卡
解决方法:使用异步下载
问题2:图片没有Frame,所有cell初始化的时候,给imageView的frame是0。异步下载完之后不显示 解决办法:使用占位图(如果展位图比较大, 自定义cell可以解决)
问题3:如果图片下载速度不一致,同时用户快速滚动的时候,会因为Cell的重用导致图片混乱
解决方法:MVC,使用Model(模型)保存下载的图像,再次刷新表格。
问题4:在用户快速滚动的时候,会重复添加下载任务到下载队列。
解决方法:建立下载操作的缓冲池。首先检查缓冲池里是否有当前图片的下载操作。有的话就不创建下载操作。从而保证一张图片只添加一个下载操作。其实就是建立一个全局的字典属性。
*/
// cell里面的imageView子控件是懒加载
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *ID = @"AppCell";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:ID];
// 给Cell设置数据
CZApp *app = self.appList[indexPath.row];
cell.textLabel.text = app.name;
cell.detailTextLabel.text = app.download;
// 判断模型里面是否有图像
if (app.image) {
NSLog(@" 图片已经下载......");
cell.imageView.image = app.image;
}else{ // 如果模型内没有图片就异步下载
// 显示图片—占位图
cell.imageView.image = [UIImage imageNamed:@"user_default"];
/**
如果下载缓冲池里面有当前图片的下载操作,就不用创建下载操作,没有才创建
缓冲池字典中 key:存放当前图片的url,字符串类型。
Value:保存下载操作
*/
if (self.operationCache[app.icon]) {
NSLog(@"正在玩命下载中......");
}else{
// 缓冲池没有下载操作
// 异步下载图片
NSBlockOperation *downLoadOp = [NSBlockOperation blockOperationWithBlock:^{
// 模拟延时
[NSThread sleepForTimeInterval:2];
NSLog(@"正在下载中......");
// 1. 下载图片(二进制数据)
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:app.icon]];
// 2. 将下载的数据保存到模型
app.image = [UIImage imageWithData:data];
// 3. 在主线程更新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// cell.imageView.image = [UIImage imageWithData:data];
// 因为Cell的图片是懒加载的方式添加的。只设置了Image但是没有设置Frame,只有调用layoutSubView()方法才会给Cell子控件重新布局
// layoutSubView是UIView的方法
// 注意:点击Cell也会触发layoutSubView()方法
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
/** reload 会重新调用cell的初始化方法, 会重新判断模型里面是否有图像
有的话就直接显示
*/
}];
}];
// 将操作添加到队列
[self.opQueue addOperation:downLoadOp];
NSLog(@"操作的数量------------->%ld", self.opQueue.operationCount);
// 将操作添加到缓冲池中(使用图片的url作为key)
[self.operationCache setObject:downLoadOp forKey:app.icon];
}
}
return cell;
}
@end</span>
运行结果如下:
添加操作缓存之前,运行并快速滑动
添加操作缓存之后,运行并快速滑动