iOS开发实践之cell下载图片(自定义NSOperation)

wjwwsw 8年前

来自: http://blog.csdn.net/zhixinhuacom/article/details/50551465


    上一篇文章的下载图片操作都放在了block中,当遇到复杂的操作,一堆的代码放在block中 ,很明显这不是明智的选择,代码显得很臃肿。 因此,把线程操作放到自定义NSOperation中。


  自定义NSOperation的步骤:继承NSOperation、重写- (void)main方法,在里面实现想执行的任务。

  重写- (void)main方法的注意点:

     1、自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)。

     2、经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应。


案例代码:

  1、新建DownloadOperation继承NSOperation,下载操作放到main方法中。 至于下载后回到主线程要做点什么,它自己本身不知道(不是它的工作范畴)。只负责下载完成后通知其它人,因此委托代理,委托别人去做事。

DownloadOperation.h

#import <Foundation/Foundation.h>  #import <UIKit/UIKit.h>    @class DownloadOperation;  //协议  @protocol DownloadOperationDelegate <NSObject>  @optional  - (void)downloadOperation:(DownloadOperation *)operation didFinishDownload:(UIImage *)image;  @end    @interface DownloadOperation : NSOperation  //下载图片地址  @property(nonatomic,copy) NSString *imageUrl;  //表格cell位置  @property(nonatomic,strong) NSIndexPath *indexPath;  //代理  @property(nonatomic,weak) id<DownloadOperationDelegate> delegate;      @end


DownloadOperation.m      

注意:@autoreleasepool 自动释放池 、isCancelled方法检测操作是否被取消,对取消做出响应。

#import "DownloadOperation.h"  #import <UIKit/UIKit.h>    @implementation DownloadOperation    -(void)main{      @autoreleasepool {//管理内存          if (self.isCancelled)  return;  //暂停为执行的操作                    NSURL *url = [NSURL URLWithString:self.imageUrl];          NSData *data = [NSData dataWithContentsOfURL:url]; // 下载          UIImage *image = [UIImage imageWithData:data]; // NSData -> UIImage                    if(self.isCancelled) return;//暂停正在执行的操作                    // 回到主线程          [[NSOperationQueue mainQueue] addOperationWithBlock:^{              if ([self.delegate respondsToSelector:@selector(downloadOperation:didFinishDownload:)]) {                  [self.delegate downloadOperation:self didFinishDownload:image];              }          }];      }  }    @end


2、AppsTableViewController.m

//  //  AppsTableViewController.m  //  cell图片下载(自定义operation)      #import "AppsTableViewController.h"  #import "App.h"  #import "DownloadOperation.h"    @interface AppsTableViewController ()<DownloadOperationDelegate>  //应用信息集合  @property(nonatomic,strong) NSMutableArray *apps;  //存放所有下载图片的队列  @property(nonatomic,strong) NSOperationQueue *queue;  //存放所有的下载操作(url是key,operation对象是value)  @property(nonatomic,strong) NSMutableDictionary *operations;  //存放所有下载完的图片  @property(nonatomic,strong) NSMutableDictionary *images;    @end    @implementation AppsTableViewController    - (void)viewDidLoad {      [super viewDidLoad];             }    - (void)didReceiveMemoryWarning {      [super didReceiveMemoryWarning];      // Dispose of any resources that can be recreated.  }    /**   * 懒加载   **/  -(NSMutableArray *)apps{      if (!_apps) {          NSMutableArray *appArr = [NSMutableArray array];          NSString *file =[[NSBundle mainBundle] pathForResource:@"apps" ofType:@"plist"];          NSArray *dictArr = [NSArray arrayWithContentsOfFile:file];          for (NSDictionary *dict in dictArr) {              App *app = [App appWithDict:dict];              [appArr addObject:app];          }          _apps = appArr;      }      return _apps;  }    -(NSOperationQueue *)queue{      if (!_queue) {          _queue = [[NSOperationQueue alloc]init];      }      return _queue;  }    -(NSMutableDictionary *)operations{      if (!_operations) {          _operations = [[NSMutableDictionary alloc]init];      }      return _operations;  }    -(NSMutableDictionary *)images{      if (!_images) {          _images = [[NSMutableDictionary alloc]init];      }      return _images;  }    #pragma mark - Table view data source    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {      return 1;  }    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {      return self.apps.count;  }      - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {      static NSString *ID = @"app";      UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];      if (!cell) {          cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];      }            App *app = self.apps[indexPath.row];      cell.textLabel.text = app.name;      cell.detailTextLabel.text = app.download;            // 先从images缓存中取出图片url对应的UIImage      UIImage *image = self.images[app.icon];      if (image) {// 说明图片已经下载成功过(成功缓存)          cell.imageView.image = image;      }else{          // 获得caches的路径, 拼接文件路径          NSString *file = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:[app.icon lastPathComponent]];                    // 先从沙盒中取出图片          NSData *data = [NSData dataWithContentsOfFile:file];          if (data) {//沙盒中存在图片              cell.imageView.image = [UIImage imageWithData:data];          }else{//沙盒不存,进行下载操作              //显示占位图片              cell.imageView.image = [UIImage imageNamed:@"placeholder"];                            // 下载图片              [self download:app.icon indexPath:indexPath];            }                }        return cell;  }    -(void)download:(NSString *)imageUrl indexPath:(NSIndexPath *)indexPath{      //取出当前图片url对应下的下载操作(operations对象)      DownloadOperation *operation = self.operations[imageUrl];      if (operation)  return; //如果存在操作就不往下执行(因为可能该图片下载操作正在进行)            //创建操作,下载图片      operation = [[DownloadOperation alloc]init];      operation.imageUrl = imageUrl;      operation.indexPath = indexPath;            //设置代理      operation.delegate = self;            // 添加操作到队列中      [self.queue addOperation:operation];             // 添加到字典中 (这句代码为了解决重复下载)      self.operations[imageUrl] = operation;            }      /**   *  当用户开始拖拽表格时调用   */  -(void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{    //暂停下载      [self.queue setSuspended:YES];  }    /**   *  当用户停止拖拽表格时调用   */  -(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{      //开始下载      [self.queue setSuspended:NO];  }    #pragma mark - 下载操作的代理方法  -(void)downloadOperation:(DownloadOperation *)operation didFinishDownload:(UIImage *)image{      // 存放图片到字典中      if (image) {          //存放所有的下载操作          self.operations[operation.imageUrl] = image;                    //将图片存入沙盒中          NSData *data = UIImagePNGRepresentation(image);          NSString *file = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)lastObject]stringByAppendingPathComponent:[operation.imageUrl lastPathComponent]];          [data writeToFile:file atomically:YES];      }            // 从字典中移除下载操作 (防止operations越来越大,保证下载失败后,能重新下载)      [self.operations removeObjectForKey:operation.imageUrl];            // 刷新表格      [self.tableView reloadRowsAtIndexPaths:@[operation.indexPath] withRowAnimation:UITableViewRowAnimationNone];    }  @end