1、NSOperation VS GCD
- GCD
- GCD是iOS4.0 推出的,主要针对多核cpu作了优化,是C语言的技术
- GCD是将任务(block)添加到队列(串行/并行/全局/主队列),而且以同步/异步的方式执行任务的函数
- GCD提供了一些NSOperation不具有的功能
- 一次性执行
- 延迟执行
- 调度组
- NSOperation
- NSOperation是iOS2.0推出的,iOS4以后重写了NSOperation
- NSOperation将操做(异步的任务)添加到队列(并发队列),就会执行指定操做的函数
- NSOperation里提供的方便的操做
- 最大并发数
- 队列的暂定/继续
- 取消全部的操做
- 指定操做之间的依赖关系(GCD能够用同步实现)
2、最大并发数
- 什么是并发数
同时执行的任务数数组
好比,同时开3个线程执行3个任务,并发数就是3缓存
- 最大并发数的相关方法
- (NSInteger)maxConcurrentOperationCount; - (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
- 执行的过程
- 一、把操做添加到队列self.queue addOperationWithBlock
- 二、去线程池去取空闲的线程,若是没有就建立线程
- 三、把操做交给从线程池中取出的线程执行
- 四、执行完成后,把线程再放回线程池中
- 五、重复2,3,4知道全部的操做都执行完
队列的暂停、取消、恢复
取消队列的全部操做 - (void)cancelAllOperations; 提示:也能够调用NSOperation的- (void)cancel方法取消单个操做 暂停和恢复队列 - (void)setSuspended:(BOOL)b; // YES表明暂停队列,NO表明恢复队列 - (BOOL)isSuspended;
摇奖机
@interface ViewController () @property (weak, nonatomic) IBOutlet UILabel *lbl1; @property (weak, nonatomic) IBOutlet UILabel *lbl2; @property (weak, nonatomic) IBOutlet UILabel *lbl3; @property (weak, nonatomic) IBOutlet UIButton *startButton; //全局队列 @property (nonatomic, strong) NSOperationQueue *queue; @end @implementation ViewController //懒加载 - (NSOperationQueue *)queue { if (_queue == nil) { _queue = [NSOperationQueue new]; } return _queue; } //点击开始执行 - (IBAction)start:(UIButton *)sender { //当队列中有操做的时候,不添加操做 if (self.queue.operationCount == 0) { //异步执行 添加操做 [self.queue addOperationWithBlock:^{ [self random]; }]; [self.startButton setTitle:@"暂停" forState:UIControlStateNormal]; self.queue.suspended = NO; }else if(!self.queue.isSuspended) { //正在执行的时候,暂停 //先把当前的操做执行完毕,暂停后续的操做 self.queue.suspended = YES; [self.startButton setTitle:@"继续" forState:UIControlStateNormal]; } } //随机生成3个数字,显示到label上 - (void)random { while (!self.queue.isSuspended) { [NSThread sleepForTimeInterval:0.05]; //生成随机数 [0,10) 0-9 int num1 = arc4random_uniform(10); int num2 = arc4random_uniform(10); int num3 = arc4random_uniform(10); //回到主线程上更新UI [[NSOperationQueue mainQueue] addOperationWithBlock:^{ //给label赋值 self.lbl1.text = [NSString stringWithFormat:@"%d",num1]; self.lbl2.text = [NSString stringWithFormat:@"%d",num2]; self.lbl3.text = [NSString stringWithFormat:@"%d",num3]; }]; } } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSLog(@"%zd",self.queue.operationCount); }
3、操做的优先级
- 设置NSOperation在queue中的优先级,能够改变操做的执行优先级
- (NSOperationQueuePriority)queuePriority; - (void)setQueuePriority:(NSOperationQueuePriority)p;
- iOS8之后推荐使用服务质量 qualityOfService
监听操做完成
- 能够监听一个操做的执行完毕
- (void (^)(void))completionBlock; - (void)setCompletionBlock:(void (^)(void))block;
@interface ViewController () @property (nonatomic, strong) NSOperationQueue *queue; @end @implementation ViewController //懒加载 - (NSOperationQueue *)queue{ if (_queue == nil) { _queue = [NSOperationQueue new]; } return _queue; } - (void)viewDidLoad { [super viewDidLoad]; //操做1 NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ for (int i = 0; i < 20; i++) { NSLog(@"op1 %d",i); } }]; //设置优先级最高 op1.qualityOfService = NSQualityOfServiceUserInteractive; [self.queue addOperation:op1]; //等操做完成,执行 执行在子线程上 [op1 setCompletionBlock:^{ NSLog(@"============op1 执行完成========== %@",[NSThread currentThread]); }]; //操做2 NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ for (int i = 0; i < 20; i++) { NSLog(@"op2 %d",i); } }]; //设置优先级最低 op2.qualityOfService = NSQualityOfServiceBackground; [self.queue addOperation:op2]; } @end
4、操做依赖
- NSOperation之间能够设置依赖来保证执行顺序
好比必定要让操做A执行完后,才能执行操做B,能够这么写网络
[operationB addDependency:operationA]; // 操做B依赖于操做A
能够在不一样queue的NSOperation之间建立依赖关系并发
模拟软件升级过程:下载—解压—升级完成app
@interface ViewController () @property (nonatomic, strong) NSOperationQueue *queue; @end @implementation ViewController - (NSOperationQueue *)queue { if (_queue == nil) { _queue = [NSOperationQueue new]; } return _queue; } - (void)viewDidLoad { [super viewDidLoad]; // 下载 - 解压 - 升级完成 NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"下载"); }]; NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ [NSThread sleepForTimeInterval:2.0]; NSLog(@"解压"); }]; NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"升级完成"); }]; //设置操做间的依赖 [op2 addDependency:op1]; [op3 addDependency:op2]; //错误,会发生循环依赖,什么都不执行 // [op1 addDependency:op3]; //操做添加到队列中 [self.queue addOperations:@[op1,op2] waitUntilFinished:NO]; //依赖关系能够夸队列执行 [[NSOperationQueue mainQueue] addOperation:op3]; }
案例-UITableView中显示图片
步骤1—数据模型准备
- 把准备好的数据源(plist)转换成咱们使用方便的对象集合
从字典类型自定绑定属性
[obj setValuesForKeysWithDictionary:dict];
提供方法—---生成全部对象的一个集合dom
+ (NSArray *)appList;
搭建界面
- UITabelViewController
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section; - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
把name和download都先显示到界面上异步
同步方式下载图片
- 在cell生成的时候下载图片
//模拟网络延时 [NSThread sleepForTimeInterval:0.5]; NSURL *url = [NSURL URLWithString:appInfo.icon]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *img = [UIImage imageWithData:data];
问题:进入界面很慢,一滑动就卡函数
缘由:同步方式去下载图片的时候,系统没法很快执行界面渲染,俗称“卡主线程”测试
问题:每次上拉下拉的时候都会重复下载图片优化
ViewController.m
#import "ViewController.h" #import "HMAppInfo.h" @interface ViewController () @property (nonatomic, strong) NSArray *appInfos; @end //1 建立模型类,获取数据,测试 //2 数据源方法 //3 同步下载图片 @implementation ViewController //懒加载 - (NSArray *)appInfos { if (_appInfos == nil) { _appInfos = [HMAppInfo appInfos]; } return _appInfos; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //1 测试模型数据 // NSLog(@"%@",self.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"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseId]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseId]; } //2 获取数据,给cell内部子控件赋值 HMAppInfo *appInfo = self.appInfos[indexPath.row]; cell.textLabel.text = appInfo.name; cell.detailTextLabel.text = appInfo.download; //同步下载图片 //模拟网速比较慢 [NSThread sleepForTimeInterval:0.5]; NSURL *url = [NSURL URLWithString:appInfo.icon]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *img = [UIImage imageWithData:data]; cell.imageView.image = img; //3 返回cell return cell; } @end
HMAppInfo.h
@interface HMAppInfo : NSObject @property (nonatomic, copy) NSString *name; @property (nonatomic, copy) NSString *icon; @property (nonatomic, copy) NSString *download; + (instancetype)appInfoWithDic:(NSDictionary *)dic; //获取全部的模型数据 + (NSArray *)appInfos; @end
HMAppInfo.m
#import "HMAppInfo.h" @implementation HMAppInfo + (instancetype)appInfoWithDic:(NSDictionary *)dic { HMAppInfo *appInfo = [[self alloc] init]; //kvc给属性赋值 [appInfo setValuesForKeysWithDictionary:dic]; return appInfo; } + (NSArray *)appInfos{ //加载plist NSString *path = [[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil]; NSArray *array = [NSArray arrayWithContentsOfFile:path]; NSMutableArray *mArray = [NSMutableArray arrayWithCapacity:10]; //便利数组的另外一种方式 [array enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL * _Nonnull stop) { //字典转模型 HMAppInfo *appInfo = [self appInfoWithDic:obj]; [mArray addObject:appInfo]; }]; //返回 对可变数组进行copy操做。变成不可变数组 return mArray.copy; } @end
异步方式下载图片
- 异步方式下载图片
//异步加载网络图片 NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ }];
- 问题1:图片显示不出,点击才显示
- 缘由:cell中的imageView是懒加载的,在初始化的时候图片没有指定,因此图片大小为0x0
- 解决:设置占位图片
ViewController.m
#import "ViewController.h" #import "HMAppInfo.h" #import "HMAppInfoCell.h" @interface ViewController () @property (nonatomic, strong) NSArray *appInfos; //全局队列 @property (nonatomic, strong) NSOperationQueue *queue; @end //1 建立模型类,获取数据,测试 //2 数据源方法 //3 同步下载图片--若是网速比较慢,界面会卡顿 //4 异步下载图片--图片显示不出来,点击cell或者上下拖动图片能够显示 //解决,使用占位图片 @implementation ViewController //懒加载 - (NSArray *)appInfos { if (_appInfos == nil) { _appInfos = [HMAppInfo appInfos]; } return _appInfos; } - (NSOperationQueue *)queue { if (_queue == nil) { _queue = [NSOperationQueue new]; } return _queue; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //1 测试模型数据 // NSLog(@"%@",self.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]; if (cell == nil) { cell = [[HMAppInfoCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseId]; } //2 获取数据,给cell内部子控件赋值 HMAppInfo *appInfo = self.appInfos[indexPath.row]; //cell内部的子控件都是懒加载的 //当返回cell以前,会调用cell的layoutSubviews方法 cell.textLabel.text = appInfo.name; cell.detailTextLabel.text = appInfo.download; //设置占位图片 cell.imageView.image = [UIImage imageNamed:@"user_default"]; //异步下载图片 //模拟网速比较慢 [self.queue addOperationWithBlock:^{ [NSThread sleepForTimeInterval:0.5]; NSURL *url = [NSURL URLWithString:appInfo.icon]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *img = [UIImage imageWithData:data]; //主线程上更新UI [[NSOperationQueue mainQueue] addOperationWithBlock:^{ cell.imageView.image = img; }]; }]; //3 返回cell return cell; } @end
HMAppInfoCell.m
#import "HMAppInfoCell.h" @implementation HMAppInfoCell - (void)layoutSubviews { [super layoutSubviews]; NSLog(@"layoutSubviews"); } @end
- 问题2:频繁滚动时,反复下载相同图片
- 解决:图片缓存,模型类中增长image的属性
- 问题3:频繁滚动时,而且超出屏幕的图片下载速度比较慢的状况,图片可能错位,而且图片来回跳
- 缘由:cell的重用
- 解决:当异步下载完成后,回到主线程从新加载对应的cell
ViewController.m
#import "ViewController.h" #import "HMAppInfo.h" #import "HMAppInfoCell.h" @interface ViewController () @property (nonatomic, strong) NSArray *appInfos; //全局队列 @property (nonatomic, strong) NSOperationQueue *queue; @end //1 建立模型类,获取数据,测试 //2 数据源方法 //3 同步下载图片--若是网速比较慢,界面会卡顿 //4 异步下载图片--图片显示不出来,点击cell或者上下拖动图片能够显示 //解决,使用占位图片 //5 图片缓存--把网络上下载的图片,保存到内存 //解决,图片重复下载,把图片缓存到内存中,节省用户的流量 (拿空间换取执行时间) //6 解决图片下载速度特别慢,出现的错行问题。 //当图片下载完成以后,从新加载对应的cell @implementation ViewController //懒加载 - (NSArray *)appInfos { if (_appInfos == nil) { _appInfos = [HMAppInfo appInfos]; } return _appInfos; } - (NSOperationQueue *)queue { if (_queue == nil) { _queue = [NSOperationQueue new]; } return _queue; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //1 测试模型数据 // NSLog(@"%@",self.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]; if (cell == nil) { cell = [[HMAppInfoCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseId]; } //2 获取数据,给cell内部子控件赋值 HMAppInfo *appInfo = self.appInfos[indexPath.row]; //cell内部的子控件都是懒加载的 //当返回cell以前,会调用cell的layoutSubviews方法 cell.textLabel.text = appInfo.name; cell.detailTextLabel.text = appInfo.download; //判断有没有图片缓存 if (appInfo.image) { NSLog(@"从缓存加载图片..."); cell.imageView.image = appInfo.image; return cell; } //设置占位图片 cell.imageView.image = [UIImage imageNamed:@"user_default"]; //异步下载图片 //模拟网速比较慢 [self.queue addOperationWithBlock:^{ // [NSThread sleepForTimeInterval:0.5]; //模拟图片下载速度慢 if (indexPath.row > 9) { [NSThread sleepForTimeInterval:5]; } //下载图片 NSLog(@"下载网络图片..."); NSURL *url = [NSURL URLWithString:appInfo.icon]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *img = [UIImage imageWithData:data]; //缓存图片 appInfo.image = img; //主线程上更新UI [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // cell.imageView.image = img; //解决图片显示错行 [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; }]; }]; //3 返回cell return cell; } @end
操做缓存池
- 问题:因为滑动过快致使屡次下载相同图片
- 解决:操做缓存
- 定义一个字典,把每次操做都放在里面,避免相同操做的出现
NSMutableDictionary *operationCaches;
@interface ViewController () @property (nonatomic, strong) NSArray *appInfos; //全局队列 @property (nonatomic, strong) NSOperationQueue *queue; //图片的缓存池 @property (nonatomic, strong) NSMutableDictionary *imageCache; //下载操做缓存池 @property (nonatomic, strong) NSMutableDictionary *downloadCache; @end //1 建立模型类,获取数据,测试 //2 数据源方法 //3 同步下载图片--若是网速比较慢,界面会卡顿 //4 异步下载图片--图片显示不出来,点击cell或者上下拖动图片能够显示 //解决,使用占位图片 //5 图片缓存--把网络上下载的图片,保存到内存--图片存储在模型对象中 //解决,图片重复下载,把图片缓存到内存中,节省用户的流量 (拿空间换取执行时间) //6 解决图片下载速度特别慢,出现的错行问题。 //当图片下载完成以后,从新加载对应的cell //7 当收到内存警告,要清理内存,若是图片存储在模型对象中,很差清理内存 //图片的缓存池 //8 当有些图片下载速度比较慢,上下不停滚动的时候,会重复下载图片,会浪费流量 //判断当前是否有对应图片的下载操做,若是没有添加下载操做,若是有不要重复建立操做 //下载操做的缓存池 @implementation ViewController //懒加载 - (NSArray *)appInfos { if (_appInfos == nil) { _appInfos = [HMAppInfo appInfos]; } return _appInfos; } - (NSOperationQueue *)queue { if (_queue == nil) { _queue = [NSOperationQueue new]; } return _queue; } - (NSMutableDictionary *)imageCache { if (_imageCache == nil) { _imageCache = [NSMutableDictionary dictionaryWithCapacity:10]; } return _imageCache; } - (NSMutableDictionary *)downloadCache { if (_downloadCache == nil) { _downloadCache = [NSMutableDictionary dictionaryWithCapacity:10]; } return _downloadCache; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //1 测试模型数据 // NSLog(@"%@",self.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]; if (cell == nil) { cell = [[HMAppInfoCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseId]; } //2 获取数据,给cell内部子控件赋值 HMAppInfo *appInfo = self.appInfos[indexPath.row]; //cell内部的子控件都是懒加载的 //当返回cell以前,会调用cell的layoutSubviews方法 cell.textLabel.text = appInfo.name; cell.detailTextLabel.text = appInfo.download; //判断有没有图片缓存 if (self.imageCache[appInfo.icon]) { NSLog(@"从缓存加载图片..."); cell.imageView.image = self.imageCache[appInfo.icon]; return cell; } //设置占位图片 cell.imageView.image = [UIImage imageNamed:@"user_default"]; //判断下载操做缓存池 中是否有对应的操做 if (self.downloadCache[appInfo.icon]) { NSLog(@"正在拼命下载图片..."); return cell; } //异步下载图片 //模拟网速比较慢 //下载操做 NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ // [NSThread sleepForTimeInterval:0.5]; //模拟图片下载速度慢 if (indexPath.row > 9) { [NSThread sleepForTimeInterval:10]; } //下载图片 NSLog(@"下载网络图片..."); NSURL *url = [NSURL URLWithString:appInfo.icon]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *img = [UIImage imageWithData:data]; //缓存图片 self.imageCache[appInfo.icon] = img; //主线程上更新UI [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // cell.imageView.image = img; //解决图片显示错行 [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; }]; }]; //把操做添加到队列中 [self.queue addOperation:op]; //把操做添加到下载操做缓存池中 self.downloadCache[appInfo.icon] = op; //3 返回cell return cell; } //接收到内存警告 - (void)didReceiveMemoryWarning { //清理内存 [self.imageCache removeAllObjects]; // [self.downloadCache removeAllObjects]; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { //点击cell的时候,输出当前队列的操做数 NSLog(@"队列的操做数:%zd",self.queue.operationCount); } @end @interface ViewController () @property (nonatomic, strong) NSArray *appInfos; //全局队列 @property (nonatomic, strong) NSOperationQueue *queue; //图片的缓存池 @property (nonatomic, strong) NSMutableDictionary *imageCache; //下载操做缓存池 @property (nonatomic, strong) NSMutableDictionary *downloadCache; @end //1 建立模型类,获取数据,测试 //2 数据源方法 //3 同步下载图片--若是网速比较慢,界面会卡顿 //4 异步下载图片--图片显示不出来,点击cell或者上下拖动图片能够显示 //解决,使用占位图片 //5 图片缓存--把网络上下载的图片,保存到内存--图片存储在模型对象中 //解决,图片重复下载,把图片缓存到内存中,节省用户的流量 (拿空间换取执行时间) //6 解决图片下载速度特别慢,出现的错行问题。 //当图片下载完成以后,从新加载对应的cell //7 当收到内存警告,要清理内存,若是图片存储在模型对象中,很差清理内存 //图片的缓存池 //8 当有些图片下载速度比较慢,上下不停滚动的时候,会重复下载图片,会浪费流量 //判断当前是否有对应图片的下载操做,若是没有添加下载操做,若是有不要重复建立操做 //下载操做的缓存池 @implementation ViewController //懒加载 - (NSArray *)appInfos { if (_appInfos == nil) { _appInfos = [HMAppInfo appInfos]; } return _appInfos; } - (NSOperationQueue *)queue { if (_queue == nil) { _queue = [NSOperationQueue new]; } return _queue; } - (NSMutableDictionary *)imageCache { if (_imageCache == nil) { _imageCache = [NSMutableDictionary dictionaryWithCapacity:10]; } return _imageCache; } - (NSMutableDictionary *)downloadCache { if (_downloadCache == nil) { _downloadCache = [NSMutableDictionary dictionaryWithCapacity:10]; } return _downloadCache; } - (void)viewDidLoad { [super viewDidLoad]; //1 测试模型数据 // NSLog(@"%@",self.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]; if (cell == nil) { cell = [[HMAppInfoCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseId]; } //2 获取数据,给cell内部子控件赋值 HMAppInfo *appInfo = self.appInfos[indexPath.row]; //cell内部的子控件都是懒加载的 //当返回cell以前,会调用cell的layoutSubviews方法 cell.textLabel.text = appInfo.name; cell.detailTextLabel.text = appInfo.download; //判断有没有图片缓存 if (self.imageCache[appInfo.icon]) { NSLog(@"从缓存加载图片..."); cell.imageView.image = self.imageCache[appInfo.icon]; return cell; } //设置占位图片 cell.imageView.image = [UIImage imageNamed:@"user_default"]; //判断下载操做缓存池 中是否有对应的操做 if (self.downloadCache[appInfo.icon]) { NSLog(@"正在拼命下载图片..."); return cell; } //异步下载图片 //模拟网速比较慢 //下载操做 NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ // [NSThread sleepForTimeInterval:0.5]; //模拟图片下载速度慢 if (indexPath.row > 9) { [NSThread sleepForTimeInterval:10]; } //下载图片 NSLog(@"下载网络图片..."); NSURL *url = [NSURL URLWithString:appInfo.icon]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *img = [UIImage imageWithData:data]; //缓存图片 self.imageCache[appInfo.icon] = img; //主线程上更新UI [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // cell.imageView.image = img; //解决图片显示错行 [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; }]; }]; //把操做添加到队列中 [self.queue addOperation:op]; //把操做添加到下载操做缓存池中 self.downloadCache[appInfo.icon] = op; //3 返回cell return cell; } //接收到内存警告 - (void)didReceiveMemoryWarning { //清理内存 [self.imageCache removeAllObjects]; [self.downloadCache removeAllObjects]; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { //点击cell的时候,输出当前队列的操做数 NSLog(@"队列的操做数:%zd",self.queue.operationCount); } @end
注意
- Block的循环引用
- 代码重构
- 把下载图片的代码提取成方法