关于tableView的显示优化

前言:相信大家都使用过SDWebImage这么一个三方框架吧.对,这个框架写的真心的不错.使用过这个框架,就一定要了解它内部的实现过程,否则糊里糊涂的使用,那天不更新了,怎么办.

这篇文章主要是介绍SDWebImage加载网络图片的基本原理.

tableView是我们做APP的时候,使用率最多的一个控件了,我敢说一个app没有tableView的话,它都不好意思说自己是个app,更别说上架了.

说到tableView,其实它本身就是一个比较好的控件,既继承了scrollView的滚动,又有缓存池这么一个特别的存储方式(重复利用) .但是一旦涉及到网络,它变有点捉襟见肘了,异步请求,主线程更新,cell高度 等等的问题,造成了它的显示问题.

下来我给大家说一说,初步的优化方案.
1.我一用户为出发点进行说明,用户肯定要使用比较省流量的客户端,如果进一次就下载一次,是不是很耗流量.

对应的,我们不应该直接从网络上直接请求数据, 应该先从用户手机的cache中取搜寻(哪个文件夹---->根据你从网络上请求的数据下载到哪. 为什么是cache,这个是苹果沙盒中就只有这个文件夹,我们还能存了,其他的不好意思,苹果会做特殊处理的 :document--->这里放东西,对不起,别想上架了.preference---> 偏好设置, 一般放配置相关的参数. temp--->临时数据(会自动删除的) )

  1. 网络请求的存放方式 ---->1>直接存放在cache.2>更新显示.
    这里对于网络请求我们也需要优化,保证每个下载只有一个进程,当图片下载完成后,要及时移除进程.如果当前cell下载已有进行,就不必去创建新的线程进行下载. 如果图片长时间无法被下载下来,要及时停止当前的任务(双进程,可能下载好了以后又会被新进程的下载覆盖)

3.对于用户,最快的方式读取数据,不应该是什么路径读取,而是自己的程序内直接读取最好.(所以网络上下载的数据应该在程序内也保存一份.)
不用担心,程序会变大,应为这一部分数据,只要已退出界面,就会马上被移除的

下载用一张图表进行一个详细的说明
如果没有沙盒的话:


关于tableView的显示优化_第1张图片
Snip20150917_1.png

如果有沙盒的话:


关于tableView的显示优化_第2张图片
Snip20150917_2.png

下面我把代码程序附带一份---->仅供参数

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"app"];
    
    AppModel *appModel = self.apps[indexPath.row];
    
    cell.textLabel.text = appModel.name;

    cell.detailTextLabel.text = appModel.download;
    
    // 占位图片
    cell.imageView.image = [UIImage imageNamed:@"kk_fruit_link_180px_1186831_easyicon.net"];
    // 从图片缓存中取, 如果取不到
    if (!self.iconCache[appModel.icon]) {  
        NSLog(@"没有缓存");
        NSString *cache = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
        NSLog(@"%@",cache);
        NSString *file = [appModel.icon lastPathComponent];
        NSString *path = [cache stringByAppendingPathComponent:file];
        
        __block NSData *data = [NSData dataWithContentsOfFile:path];
        // 从内存中取,如果取不到
        if (data == nil) {
            NSLog(@"没有数据");
            
            NSOperationQueue *queue = [[NSOperationQueue alloc]init];
            NSBlockOperation *blockOpertion = self.blockOperation[appModel.icon];
            // 判断是否被下载,若果没有下载线程
            if (blockOpertion == nil) {
                NSLog(@"没有数据,没有线程");
                blockOpertion = [NSBlockOperation blockOperationWithBlock:^{
                    
                    NSURL *url = [NSURL URLWithString:appModel.icon];
                    
                    data = [NSData dataWithContentsOfURL:url];
               // 有线程,但没有下载下来的话,删除线程,重新下载 (保证一个图片,只有一个线程来控制)
                    
                if (data == nil) {
                    NSLog(@"下载不下来,需要重新下载");
                    [self.blockOperation removeObjectForKey:appModel.icon];
                    return ;
                }
                // 下载下来的数据,返回图片
                UIImage *image = [UIImage imageWithData:data];
                // 图片放入缓存
                self.iconCache[appModel.icon] = image;
                // 数据进行储存
                [data writeToFile:path atomically:YES];
                
                // 将图片返回主线程,并赋值UI
                [[NSOperationQueue mainQueue]addOperationWithBlock:^{
                    //                    cell.imageView.image = self.iconCache[appModel.icon];
                    
                    // 刷新数据  ,不让数据复用
                    [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
                    // 下载成功移除操作
                    [self.blockOperation removeObjectForKey:appModel.icon];
                    }];
                }];
                
                // 添加操作到队列中
                [queue addOperation:blockOpertion];
            }   
        }else{
            // 如果内存中有数据
            NSLog(@"没有缓存,但有数据");
            // 取出数据,返回图片
        UIImage *image = [UIImage imageWithData:data];
            // 图片放入缓存
            self.iconCache[appModel.icon] = image;
            // UI赋值
            cell.imageView.image = self.iconCache[appModel.icon];
        }
    }
    else{
        // 如果有缓存。直接赋值
        NSLog(@"有缓存");
        cell.imageView.image = self.iconCache[appModel.icon];   
    }
  return cell;
   }

这里我要说明的是,我是以模型为基本跨度,用模型属性参数,作为键, 用来保存任务,以及程序的缓存. 并且也组成文件存储的全路径. 这样的话,比较方便存取数据. 因为本来就是要取出模型数据,因为模型数据的不同,而赋了不同的值. 又方便,又具有区分性.

这里建立了两个字典 : 1.iconCache:保存缓存的图片
2.blockOperation 用来保存 下载任务 (程序里面有一个没有定义好 ,结果有个很像的名字NSBlockOperation *blockOpertion 这个是定义任务的. 对不起各位了).

说到这里,总算将网络处理这方面讲的差不多了, 现在就来说说SDWebImage的强大.

SDWebImage的操作

对于以上的程序在SDWebImage中只需要一句代码就已经处理完全了.
在它的"UIImageView+WebCache.h"中已经将这部分代码已经封装完全了.

[cell.imageView sd_setImageWithURL:[NSURL URLWithString:appModel.icon] placeholderImage:image];
tableView的二次优化

这里就需要用到绘图的知识了.什么是imageView,其实它根本就是在一个view的上面图层上进行绘画,将图片画上去,然后将用户的交互给取了而已. 那我们是不是可以将cell 的imageView也给取了,然后只建立一个空白的view,在上面绘画就行了. 这样就会相比与以前少了一些imageview. 相当于 节省了很多控件吧. (性能相对也会减少不少).

我曾经想过一个问题, 比如绘画 (会不会很耗时), 其实imageView何尝不是一个耗时的操作(它又要创建,又要画), 从这里就可以看出,绘画是一个不错的选择.

可能大家觉得绘画,多么麻烦呀. 不错确实很麻烦,特别是cell的高度不确定得时候,会显得额外麻烦.但是我想你的服务器也不会傻到不给你传想过图片的宽高吧, 对于多组图片你利用9宫格思想将其缩小绘画(又美观又节省控件和空间).

如果imageview真的很多的时候,绘画的优势就体现出来了,因为控件加载的少了,相当于系统的消耗就会变得很少,系统就会流畅了很多.

这段代码我就不着急写了. 谢谢了. (我想说程序是有感情的,有思想的, 他们的这些,其实都是我们程序员所赋予的, 所以少年不断的学习吧, 让自己喜欢上思考,喜欢上编程吧, 让自己的程序有感情吧).

你可能感兴趣的:(关于tableView的显示优化)