zoukankan      html  css  js  c++  java
  • iOS开发>学无止境

    作者:勤奋的笨老头

    网址:http://www.jianshu.com/p/02ab2b74c451

    最近研究了一下UITbleView中异步加载网络图片的问题,iOS应用经常会看到这种界面。一个tableView上显示一些标题、详情等内容,在加上一张图片。这里说一下这种思路。

    为了防止图片多次下载,我们需要对图片做缓存,缓存分为内存缓存于沙盒缓存,我们当然两种都要实现。

    由于tableViewCell是有重用机制的,也就是说,内存中只有当前可见的cell数目的实例,滑动的时候,新显示cell会重用被滑出的cell对象。这样就存在一个问题:

    一般情况下在我们会在cellForRow方法里面设置cell的图片数据源,也就是说如果一个cell的imageview对象开启了一个下载任务,这个时候该cell对象发生了重用,新的image数据源会开启另外的一个下载任务,由于他们关联的imageview对象实际上是同一个cell实例的imageview对象,就会发生2个下载任务回调给同一个imageview对象。这个时候就有必要做一些处理,避免回调发生时,错误的image数据源刷新了UI。

    所以在我们向下滑动tableview的时候我们需要手动去取消掉下载操作,当用户停止滑动,再去执行下载操作。SDWebImage采用的也是这种策略。

    很简单我们只需要监听ScrollView的代理方法(tableview继承自Scrollview)。

    /**

    * 当用户开始拖拽表格时调用

    */

    - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView

    {

    // 暂停下载

    [self.queue setSuspended:YES];

    }

    /**

    * 当用户停止拖拽表格时调用

    */

    - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate

    {

    // 恢复下载

    [self.queue setSuspended:NO];

    }

    而SDWebImage才用的就是这种方式,所以在运行SDWebImage的demo的时候,可以看到,如果快速滑下去,然后又滑回来的话,图片是过了一会才显示出来,这是因为快速滑动的时候,旧数据源的下载任务被取消掉了。

    /-------------------------------------------

    下面介绍一下具体的思路。

    异步下载图片我们用的是NSOperation,并且创建一个全局的queue来管理下载图片的操作。

    /**

    * 存放所有下载操作的队列

    */

    @property (nonatomic,strong) NSOperationQueue* queue;

    另外需要两个字典operations、images

    /**

    * 存放所有的下载操作(url是key,operation对象是value)

    */

    @property (nonatomic,strong) NSMutableDictionary* operations;

    /**

    * 存放所有下载完成的图片,用于内存缓存,同样用Url为key

    */

    @property (nonatomic,strong) NSMutableDictionary* images;

    在把图片显示到Cell上之前:

    先判断内存中(images字典中)有没有图片,
    如果有,则取出url对应的图片来显示,
    如果没有,再去沙盒缓存中查看,当然存到沙盒中都是NSData。

    如果沙盒缓存中有,我们取出对应的数据给Cell去显示
    如果沙盒中也没有图片,我们先显示占位图片。再创建operation去执行下载操作了。
    当然在创建operation之前,我们要判断这个operation操作是否存在

    这个时候就用到我们operations这个字典了

    //取出当前URL对应的下载操作

    NSBlockOperation* operation = self.operations[app.icon];

    如果没有下载操作,我们才需要真正的去创建operation执行下载。

    创建好下载操作之后应该把该操作存放到全局队列中去异步执行,同时吧操作放入operations字典中记录下来。

    //添加操作到队列中

    [self.queue addOperation:operation];

    //添加到字典中

    self.operations[app.icon] = operation;

    下载完成之后:
    把下载好的图片放到内存中、同时存到沙盒缓存中

    下面存放到沙盒中的代码可以定义成宏,具体可以下载后面的demo

    if (image) { //防止下载失败为空赋值造成崩溃

    vc.images[app.icon] = image;

    //下载完成的图片存入沙盒中

    // UIImage --> NSData --> File(文件)

    NSData* ImageData = UIImagePNGRepresentation(image);

    NSString* CachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];

    NSString* filePath = [CachesPath stringByAppendingPathComponent:[app.icon lastPathComponent]];

    [ImageData writeToFile:filePath atomically:YES];

    }

    执行完上面的操作之后回到主线程刷新表格,

    从operations字典中移除下载操作(防止operations越来越大,同时保证下载失败后,能重新下载)

    //刷新当前行的图片数据

    self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];

    这里我们不用[self.tableView reloadata],因为会刷新整个cell,浪费性能。

    当然如果你的下载操作里面需要做的事情很多的时候,可以考虑自定义operation。 由于我这里只是简单的下载小图就没有自定义operation了。注意要自己创建自动释放池。


    另外清理缓存的功能也没有做
    有兴趣的朋友可以自己尝试下

    渣渣代码下载:https://github.com/hongfenglt/CellDownloadImage

    至此基本实现思路已经完成了,这时候你会发现其实真正在项目中,以上代码并没有什么卵用。因为这个操作,SDWebImgae已经都封装好,用一句代码就搞定了,并且这句代码代表的内容比上面的操作更加完善。⊙﹏⊙‖∣°

    以上内容只是简单的优化,想更深层去优化操作可以参考下面这篇文章:http://ios.jobbole.com/82374

    本内容来自: 超越昨天(学无止境) - http://www.cnblogs.com/xvewuzhijing/
  • 相关阅读:
    03_ if 练习 _ little2big
    uva 11275 3D Triangles
    uva 12296 Pieces and Discs
    uvalive 3218 Find the Border
    uvalive 2797 Monster Trap
    uvalive 4992 Jungle Outpost
    uva 2218 Triathlon
    uvalive 3890 Most Distant Point from the Sea
    uvalive 4728 Squares
    uva 10256 The Great Divide
  • 原文地址:https://www.cnblogs.com/xvewuzhijing/p/5003748.html
Copyright © 2011-2022 走看看