多图片下载 之 SDWebImage

首先先了解一下我们多图片下载的一般解决方案

注:以下模拟AppStore浏览购买项目场景

主体思路

主体思路

场景还原:

进入app,当图片还没显示完成时,我们不停的拖拽tableview,就会不停的调用 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath. 导致以下俩个问题.

出现问题:

  1. 重复下载操作
  2. cell重用导致图片错位
    第一次进入app我们会进行下载图片,由于下载图片是一个耗时操作,此时用户进行拖拽,由于cell的重用机制移出屏幕的cell会被重用,之前的cell会被重用到下方,恰巧图片又刚好下载完成,这时候如果我们任然进行显示
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
    cell.imageView.image = image;
 }];

就会出现 --- 植物大战僵尸2 捕鱼 都出现了错乱

cell重用导致错乱

解决办法:

1.为了防止图片下载操作重复,我们将对应的操作对象做记录,无论是成功还是失败最后我们都移除该对象,保证操作的唯一性.

/** 内存缓存的图片 */
@property (nonatomic, strong) NSMutableDictionary *images;

/** 所有的操作对象 */
@property (nonatomic, strong) NSMutableDictionary *operations;

2.下载完成我们去刷新对应的cell

// 回到主线程显示图片
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
     [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}];

整体实现代码

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *ID = @"app";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];

    XMGApp *app = self.apps[indexPath.row];
    cell.textLabel.text = app.name;
    cell.detailTextLabel.text = app.download;

    // 先从内存缓存中取出图片
    UIImage *image = self.images[app.icon];
    if (image) { // 内存中有图片,显示图片
        cell.imageView.image = image;
    } else {  // 内存中没有图片,检查沙盒中是否有图片

        // 获得Library/Caches文件夹
        NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
        // 获得文件名
        NSString *filename = [app.icon lastPathComponent];
        // 计算出文件的全路径
        NSString *file = [cachesPath stringByAppendingPathComponent:filename];
        // 加载沙盒的文件数据
        NSData *data = [NSData dataWithContentsOfFile:file];

        if (data) { // 沙盒中有图片,直接利用沙盒中图片
            UIImage *image = [UIImage imageWithData:data];
            cell.imageView.image = image;
            // 存到字典中
            self.images[app.icon] = image;
        } else { // 沙盒中没有图片,下载图片
            cell.imageView.image = [UIImage imageNamed:@"placeholder"];

            //查询是否有下载任务
            NSOperation *operation = self.operations[app.icon];
            if (operation == nil) { // 这张图片暂时没有下载任务
                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:^{
                        [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
                    }];

                    // 将图片文件数据写入沙盒中
                    [data writeToFile:file atomically:YES];
                    // 移除操作
                    [self.operations removeObjectForKey:app.icon];
                }];

                // 添加到队列中
                [self.queue addOperation:operation];

                // 存放到字典中
                self.operations[app.icon] = operation;
            }
        }
    }

    return cell;
}


小结:由于自己去完成多图片的下载 细节比较多,需要考虑cell重用错乱,重复下任务,网络错误data为空等等一系列的细节问题,为了提高开发效率我们通常会使用第三方框架


最终解决方案

第三方框架 SDWebImage

  • 导入头文件
#import "UIImageView+WebCache.h"
  • 加载图片设置占位图
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:app.icon] placeholderImage:[UIImage imageNamed:@"placeholder"]];

对照

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *ID = @"app";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];

    XMGApp *app = self.apps[indexPath.row];
    cell.textLabel.text = app.name;
    cell.detailTextLabel.text = app.download;

    //只需要一句话
    [cell.imageView sd_setImageWithURL:[NSURL URLWithString:app.icon] placeholderImage:[UIImage imageNamed:@"placeholder"]];

    return cell;
}

而且它也帮我们完成了缓存的处理

缓存图片

让我们简单的了解一下如何使用这个强大的第三方框架

1.下面我们以参数较多的方法举例:

 [cell.imageView sd_setImageWithURL:[NSURL URLWithString:app.icon] placeholderImage:[UIImage imageNamed:@"placeholder"] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {
        // expectedSize: 图片的总字节数
        // receivedSize: 已经接收的图片字节数

        NSLog(@"下载进度:%f", (double)receivedSize / expectedSize);
    } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
        NSLog(@"下载完图片");
    }];

1,sd_setImageWithURL获取网络图片
2,placeholderImage占位图片
3,progress 下载进度 用法: NSLog(@"下载进步:%f",(double)receivedSize / expectedSize);
4, *image *error *imageURL分别完成后返回 的图片,错误和下载地址
5,SDImageCacheType cacheType 是枚举类型,图片存储位置在内存、磁盘或无
6,SDWebImageOptions 枚举类型
    用法:SDWebImageOptions options = SDWebImageRetryFailed | SDWebImageLowPriority
    SDWebImageRetryFailed 下载失败重复下载        常用
    SDWebImageLowPriority 当UI交互的时候暂停下载   常用
    SDWebImageCacheMemoryOnly 只存图片在内存
    SDWebImageProgressiveDownload 可以像浏览器那样从上往下下载刷新图片
    SDWebImageRefreshCached 刷新缓存
    SDWebImageHighPriority  高优先级
    SDWebImageDelayPlaceholder 不加载占位图

options参数图片

options参数图片

2.内存处理

因为SDWebImgae是属于整个项目,不是属于某个控制器,所以不要在控制器里的didReceiveMemoryWarning处理内存问题,而且在AppDelegate.m添加applicationDidReceiveMemoryWarning方法

  • AppDelegate中 (√)
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
    // 取消所有下载
    [[SDWebImageManager sharedManager] cancelAll];
    // 清除内存缓存
    [[SDWebImageManager sharedManager].imageCache clearMemory];
}
  • 当前控制器中 出现内存警告(×)
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    self.images = nil;
    self.operations = nil;
    [self.queue cancelAllOperations];
}

3.其他功能

  • SDWebImage的图片缓存周期是多长:1个星期
默认缓存周期1周
//设置100天,默认是7天
[SDWebImageManager sharedManager].imageCache.maxCacheAge = 100 * 24 * 60 * 60
  • SDWebImage的图片最大尺寸(字节)
图片最大尺寸(字节)
//无默认值,单位字节
[SDWebImageManager sharedManager].imageCache.maxCacheSize = ;

拓展:只下载图片不设置 --- 给Button设置图片时可以使用

[[SDWebImageManager sharedManager] downloadImageWithURL:<#(NSURL *)#> options:<#(SDWebImageOptions)#> progress:^(NSInteger receivedSize, NSInteger expectedSize) {

    } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {

    }]
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 15,469评论 4 61
  • SDWebImage托管在github上。https://github.com/rs/SDWebImage 这个类...
    XLsn0w阅读 804评论 0 0
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,633评论 30 472
  • 痴女阿三(上) 从楼上往下看,对面的小院里有一畦菜园,刚被松了土,墙根下挤挤挨挨的...
    欣雅_47ff阅读 899评论 1 4
  • Android中提供了View进行绘图处理,View可以满足大部分的绘图需求,但是有时候,View却显得力不从心,...
    冰鉴IT阅读 87,382评论 19 159

友情链接更多精彩内容