自定义NSOperation实现多图片下载

界面效果图
自定义NSOperation实现多图片下载_第1张图片
Snip20170518_3.png
plist文件

文字及图片的地址从plist文件中进行读取,plist文件如下图所示:


自定义NSOperation实现多图片下载_第2张图片
Snip20170518_2.png
实现思路流程图
自定义NSOperation实现多图片下载_第3张图片
Snip20170518_8.png
具体实现代码

1、根据结构,我们自定义一个数据模型文件:

//.h文件
#import 
@interface App : NSObject
//名字
@property(nonatomic,copy)NSString *name;
//图标
@property(nonatomic,copy)NSString *icon;
//下载量
@property(nonatomic,copy)NSString *download;
+(instancetype)appWithDict:(NSDictionary *)dict;
@end

//.m文件
#import "App.h"
@implementation App
+(instancetype)appWithDict:(NSDictionary *)dict
{
    App *app = [[self alloc]init];
    [app setValuesForKeysWithDictionary:dict];
    return app;
}
@end

2、在视图控制器中的代码实现:

#import "ViewController.h"
#import "App.h"

@interface ViewController ()
/** 所有数据 */
@property(nonatomic,strong)NSArray *apps;
/** 内存缓存的图片 */
@property(nonatomic,strong)NSMutableDictionary *images;
/** 所有操作(用来存储下载图片的线程操作的字典,主要作用是防止重复下载) */
@property(nonatomic,strong)NSMutableDictionary *operations;
/** 队列对象 */
@property(nonatomic,strong)NSOperationQueue *queue;
@end

@implementation ViewController

#pragma mark 初始化方法
-(NSArray *)apps
{
    if (!_apps) {
        NSArray *dictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil]];
        NSMutableArray *apps = [NSMutableArray array];
        for (NSDictionary *dict in dictArray) {
            [apps addObject:[App appWithDict:dict]];
        }
        _apps = apps;
    }
   return _apps;
}
-(NSMutableDictionary *)images
{
    if (!_images) {
        _images = [NSMutableDictionary dictionary];
    }
    return _images;
}
-(NSMutableDictionary *)operations
{
    if (!_operations) {
        _operations = [NSMutableDictionary dictionary];
    }
    return _operations;
}
-(NSOperationQueue *)queue
{
    if (!_queue) {
        _queue = [[NSOperationQueue alloc]init];
        //控制最大并发数为3
        _queue.maxConcurrentOperationCount = 3;
    }
    return _queue;
}

#pragma mark-
-(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];
    
    App *app =  self.apps[indexPath.row];
    cell.textLabel.text = app.name;
    cell.detailTextLabel.text = app.download;
    
    if (self.images[app.icon])// 如果内存中有图片,则使用内存中的图片
    {
        cell.imageView.image = self.images[app.icon];
        
    }else{// 如果内存中没有图片,则看看沙河中是否有图片

    // 获取文件的全路径
    NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask , YES)lastObject] stringByAppendingPathComponent:[app.icon lastPathComponent]];
    // 加载沙盒的文件数据
    NSData *data = [NSData dataWithContentsOfFile:filePath];
       if (data) {// 如果数据存在则直接加载沙盒中图片
           UIImage *image = [UIImage imageWithData:data];
           cell.imageView.image = image;
           // 把图片保存到内存缓存
           self.images[app.icon] = image;
       }else{ // 如果内存和沙河中都没有图片,则下载图片
           
           NSOperation *operation = self.operations[app.icon];
           
           if (!operation) {//检查该图片时候正在下载,如果不是则再添加下载任务
               
               //subTitle类型的cell,默认imageView如果在加载cell的时候没有设置图片那就默认不存在,不会显示,所以得设置一个占位图片,在image加载完毕后可以有地方显示
               cell.imageView.image = [UIImage imageNamed:@"placeholder"];//占位图片
               //开一条线程进行图片下载操作
               operation = [NSBlockOperation blockOperationWithBlock:^{
                   NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:app.icon]];
                   //如果数据加载失败
                   if (data==nil) {
                       //移除操作
                       [self.operations removeObjectForKey:app.icon];
                       return ;
                   }
                   UIImage *image =  [UIImage imageWithData:data];
                   //把图片保存到内存缓存
                   self.images[app.icon] = image;
                   //回到主线程显示图片
                   [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                       //会出现重复占位的问题
                       //cell.imageView.image = image;
                       //刷新一行数据,解决重复占位问题(因为图片已经保存到内存缓存中,则会去内存中加载图片)
                       [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
                   }];
                   // 将图片文件数据写入沙盒中
                   [data writeToFile:filePath atomically:YES];
                   //移除图片的下载操作
                   [self.operations removeObjectForKey:app.icon];
               }];
               //添加到队列中
               [self.queue addOperation:operation];
               //添加到字典中
               self.operations[app.icon] = operation;
           }
       }
    }
    return cell;
}
#pragma mark-内存警告处理
-(void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    //内存警告的处理
    [self.queue cancelAllOperations];
    self.operations = nil;
    self.apps = nil;

}
@end
细节总结
  • 下载操作在子线程进行,解决UI不流畅问题
  • 将每一个下载任务与一个图片的url对应,通过判断图片是否正在下载,解决重复下载问题,
  • 处理数据加载失败问题。
  • 通过设置占位图片,解决因网络数据加载慢导致的图片不显示问题。
  • 通过刷新一行数据来显示UI界面,解决重复占位(图片数据错乱)问题。

你可能感兴趣的:(自定义NSOperation实现多图片下载)