关于tableView的显示优化

作者: Little_Dragon | 来源:发表于2015-09-17 10:20 被阅读2479次

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

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

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

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

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

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

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

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

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


    Snip20150917_1.png

    如果有沙盒的话:


    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真的很多的时候,绘画的优势就体现出来了,因为控件加载的少了,相当于系统的消耗就会变得很少,系统就会流畅了很多.

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

    相关文章

      网友评论

      • 甘蔗222:代码是怎么贴上去的 ?我想用简书做笔记,但是怎么放到简书上有这种效果啊。
        Little_Dragon:@gaoze markdown 模式写的笔记. ```objc 代码 ```` ##标题 等等功能. 你可以看看markdown的编译器功能
      • 只爱Ftype:你好, 刚入门做ios app ,有个疑问,请问微信这类app,是怎么处理头像图片的呢?因为我在仿做上传头像这个功能的时候只对图片做了压缩质量而没有做裁剪大小的处理,所以头像在好友列表的比较小的frame里显示的时候,图片质量显得过于清晰,所以在上传的时候是不是除了压缩图片质量,还得做裁剪大小的处理去适应好友列表cell里面的imageView的大小?如果是要裁剪的话,用户点击头像缩略图查看头像大图的时候,这个大图是由裁剪过的图直接拉伸过来还是上传的时候也保存了未经裁剪的大图?
        只爱Ftype:@風無 那也就是说用户在选定一张头像图片之后,调用ios自己的方法将图片缩放为不同的尺寸,然后将这些不同尺寸的图片一起上传,需要使用的时候,再根据场景的不同,从后台调用相应尺寸的图片?
        Little_Dragon:@只爱Ftype 上传的图片是裁剪过的。然后格式转换,压缩进行传输的。
        Little_Dragon:@只爱Ftype 这个是对图片做了二次处理,转换格式,并压缩成二进制文件进行传输的。

      本文标题:关于tableView的显示优化

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