多图下载代码

作者: Z了个L | 来源:发表于2016-03-15 00:03 被阅读188次
  • LZAppItem模型类
// LZAppItem.h
#import <Foundation/Foundation.h>

@interface LZAppItem : NSObject

/** 名称*/
@property (nonatomic ,strong) NSString *name;
/** 图标的地址*/
@property (nonatomic ,strong) NSString *icon;
/** 下载量*/
@property (nonatomic ,strong) NSString *download;

+(instancetype)appItemWithDict:(NSDictionary *)dict;

@end

// LZAppItem.m
#import "LZAppItem.h"

@implementation LZAppItem

+ (instancetype)appItemWithDict:(NSDictionary *)dict
{
    // 创建对象
    LZAppItem *appItem = [[LZAppItem alloc] init];
    // KVC
    [appItem setValuesForKeysWithDictionary:dict];
    // 返回对象
    return appItem;
}

@end
  • ViewController
// ViewController.h
#import <UIKit/UIKit.h>

@interface ViewController : UITableViewController


@end

// ViewController.m
#import "ViewController.h"
#import "LZAppItem.h"

@interface ViewController ()
/** 队列*/
@property (nonatomic ,strong) NSOperationQueue *queue;
/** 模型数组*/
@property (nonatomic, strong) NSArray *apps;
/** 图片缓存*/
@property (nonatomic ,strong) NSMutableDictionary *images;
/** 操作*/
@property (nonatomic ,strong) NSMutableDictionary *operations;
@end

@implementation ViewController

#pragma mark - 懒加载数据
- (NSArray *)apps
{
    if (_apps == nil) {
        // 1获取字典数组
        // 1.1获取路径
        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil];
        // 1.2获取到字典数组
        NSArray *dictArray = [NSArray arrayWithContentsOfFile:filePath];
        // 2.拿到模型数组
        // 2.1创建一个可变数组
        NSMutableArray *temp = [NSMutableArray array];
        // 2.2创建for循环
        for (NSDictionary *dict in dictArray) {
            LZAppItem *item = [LZAppItem appItemWithDict:dict];
            [temp addObject:item];
        }
        _apps = temp;
    }
    return _apps;
}

#pragma mark - 图片缓存
- (NSMutableDictionary *)images
{
    if (_images == nil) {
        _images = [NSMutableDictionary dictionary];
    }
    return _images;
}

#pragma mark - 队列
- (NSOperationQueue *)queue
{
    if (_queue == nil) {
        _queue = [[NSOperationQueue alloc] init];
        _queue.maxConcurrentOperationCount = 3;
    }
    return _queue;
}

#pragma mark - UITableViewDataSource方法
// 返回多少组
- (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 == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
    }

    // 拿到模型数据
    LZAppItem *item = self.apps[indexPath.row];
    // 赋值
    cell.textLabel.text = item.name;
    cell.detailTextLabel.text = item.download;

    // 赋值图片,重点

    // 检查缓存,用图片的地址做键
    UIImage *image = [self.images objectForKey:item.icon];
    if (image) { // 有内存缓存,即身上有钱
        // 直接赋值
        cell.imageView.image = image;
//        NSLog(@"%zd使用了内存缓存",indexPath.row);
    }else { // 没有内存缓存,即身上没有钱

        // 获取磁盘缓存路径
        // 0.0获取路径
        NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        // 0.1得到图片的名称
        NSString *fileName = [item.icon lastPathComponent];
        // 0.2拼接文件的路径
        NSString *fullPath = [cachePath stringByAppendingPathComponent:fileName];

        // 去检查磁盘缓存,这里找到的是保存在cache里面的NSData数据
        NSData *imgData = [NSData dataWithContentsOfFile:fullPath];

        if (imgData) { // 如果磁盘缓存里面有该图片,磁盘缓存即是卡里面有钱
            // 拿到Image
            UIImage *image = [UIImage imageWithData:imgData];
            cell.imageView.image = image;

            // 把图片保存到身上,内存缓存
            [self.images setObject:image forKey:item.icon];

//            NSLog(@"%zd使用了磁盘缓存",indexPath.row);

        } else { // 如果磁盘缓存里面没有该图片,磁盘缓存即是卡里面没有钱
            // 来到这里,说明又没有内存缓存,又没有磁盘缓存
            // 清空图片或者是设置占位图片,目的是什么,因为cell是重复利用的,假设当你第一张图片显示完毕的时候,用户继续往下拖拽,下面的cell是由上面消失的cell重复利用过来的,而下面的cell去下载图片的时间可能比较长,所以显示的效果是上一张残留下来的图片,之后再把从网络下载的图片进行覆盖,也就是图片错乱了,所以,为了防止这个问题,用一张占位图片解决
            cell.imageView.image = [UIImage imageNamed:@"Snip20200808_172"];

            /*避免重复操作,当程序第一次运行起来的时候,显示第一个cell的时候,创建一个操作去服务器端获取数据,然后用户又随便往下拖拽,那么第一个cell就不再显示了,此时那个获取数据的操作还在执行,假设需要10秒,然后用户又随便拖拽,滚到了第一个cell,第一个cell又重新显示出来了,那么它还会继续创建一个操作去服务器端获取数据,那么可能就有好几个操作发送到服务器端去获取同一个数据了,没有必要,所以,这里采用了一个可变字典,用来判断,好办法,谁想出来的,牛逼*/
            // 检查操作缓存
            NSBlockOperation *dowbloadOperation = [self.operations objectForKey:item.icon];
            if (dowbloadOperation) { // 如果有操作,说明之前已经发了一次操作过去了,那么再次来到这的时候,就不能再发请求了,所以什么也不能做

            } else { // 如果没有操作,说明之前没有发过操作过去,要添加操作
                    dowbloadOperation = [NSBlockOperation blockOperationWithBlock:^{


                    // 1.创建url
                    NSURL *url = [NSURL URLWithString:item.icon];
                    // 2.拿到二进制数据
                    // 该方法通过url获取数据是有时间限制的,30秒,如果失败,返回nil
                    NSData *data = [NSData dataWithContentsOfURL:url];
                    // 3.转化为UIImage对象
                    UIImage *image = [UIImage imageWithData:data];
                    // 4.判断
                    if (image == nil) {
                        // 把这次操作删除掉
                        [self.operations removeObjectForKey:item.icon];
                        return ;
                    }
                    // 5.保存到内存缓存
                    [self.images setValue:image forKey:item.icon];
                    // 6.保存到磁盘缓存
                    [data writeToFile:fullPath atomically:YES];

                    NSLog(@"%zd直接下载",indexPath.row);

                    // 设置图片
                   // 下面这行代码应该放在主线程,那么应该回到主线程
                    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                        // 下面这个block块里面的代码是在主线程执行的
                        // 如果没有刷新操作,那么不会显示图片,为什么,因为你虽然赋值了,但是没有
                        // 调用它的layoutsubviews创建尺寸,只有调用layoutsubviews才会显示出图片
                        // 也就是刷新,或者点击某一行的时候,它会触发layoutsubviews方法,那么才会
                        // 显示出图片了,但是,你用reloadData又没有必要,只需要设置某一张图片,你刷新
                        // 整个可视区域,那就傻逼了,所以,这里面最好的方式是,刷新特定的行数
//                        cell.imageView.image = image;
//                        [tableView reloadData];
                        [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationBottom];
                    }];

                }];

                // 添加操作到内存缓存中
                [self.operations setObject:dowbloadOperation forKey:item.icon];

                // 添加到队列
                [self.queue addOperation:dowbloadOperation];
            }

        }
    }

    // 返回cell
    return cell;
}

- (void)didReceiveMemoryWarning
{
    // 移除内存缓存
    [self.images removeAllObjects];
    // 取消队列中的操作
    [self.queue cancelAllOperations];
}
@end

  • 效果图片:

相关文章

网友评论

  • 紫星轩:使用SDWebimage直接有缓存了,还需要这么麻烦吗?不过写的很好,学到了不少
  • 94fdc9682e01:不错不错!收藏了

本文标题:多图下载代码

本文链接:https://www.haomeiwen.com/subject/cqoflttx.html