热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

iOS常见问题之异步加载图片在UITableView里显示时的奇异的现象

(iphoneipad)关于uitableview自定义cell与重用机制探究(由于文章写的太好,直接转帖备用)今天在研究SDWebImage和ASIHTTP

(iphone/ipad)关于uitableview自定义cell与重用机制探究

(由于文章写的太好,直接转帖备用)  


今天在研究SDWebImage和ASIHTTPRequest实现网络图片异步加载和本地缓存的时候,在UITableView显示图片的时候,出现了一些奇异的现象,比如

  1、TableView一次只能显示10行的图片,在所有图片都加载完后,滚动TableView,让隐藏在下面的行显示在屏幕上,而这些行(比如11行)的图像会先显示第1行的图片,然后在显示属于它自己的图片。以此类推,后面的行都会出现这样的问题!! 即使我们在所有行的图片都还没有下载完成的时候,滚动TableView,让第11行、12行等出现在屏幕上,但它们依旧会先显示错误的图片,然后再显示正确的图片。

  2、在ASIHTTPRequest的Demo中,当图片加载后,滑动TableView,整个TableView的图片将会乱掉,整个TableView以循环的方式显示最后几行的图片。

     

  一番查找后,发现之所以会出现这些问题,是因为我忽略了UITableView的重用机制的影响。进过适当的修改后,demo终于能够正常运行了。下面是一些相关的资料和解决方法。

       


iphone重用机制是苹果为了实现大量数据显示而采用的一种节省内存的机制,比如在UITableView和ScrollView 等地方。为什么要“可重用”???对于我们的项目来说,内存控制是必不可少的,如果一个tableview有几百个cell,这个内存消耗是很大的,而且有些cell里面都有image之类的很占内存的资源存在的话,那这样很容易出现memory warning甚至crash掉,这不是我们想要看到的。对此,tableview实现了它自己的管理方法dequeueReusableCellWithIdentifier(ps:我们在某些项目中scrollview来显示很多张image,在scrollview滑动中也要这样处理,来避免内存的过度消耗,只不过tableview它已经实现了这个方法,而不用我们自己去写)。

但是在实际使用过程中,会有以下问题:

1、(苹果文档中不鼓励我们在UITableViewCell中添加subView,最好采用自定义Cell,将需要的SubView添加到Cell当中。)使用addSubView在每项上添加视图的时候会有重叠的现象。例如,UITableView中的Cell ,如果在cell上添加子视图,则在使用苹果的重用机制的时候,会重现子试图重叠的现象。或出现开头提到的两个问题。如果在数据量不是很多的时候,可以手动屏蔽掉UITableView的重用机制。

这里不得不提一下UITableView的重用机制:

UITableView的重用机制的实现关键在于下面这个的函数:

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

首先,我们要清楚这一点,这个函数是做什么的,它的文档说明如下:
returns a reusable table-view cell object located by its identifier。它返回的是一个受identifier管理定位的可重用的tableViewCell,这里重点就在于“可重用”这3个字上。

我们来看它的实现方法,举个例子来说,在系统刚启动时,tableview可以显示多少个cell,在这里我们假定为10个,在刚开始的时候tableview会生成10个tableviewcell,并且对应有自己的tag值,假定为0-9。(ps:苹果官方的视频中也提到了,尽量避免频繁的add/remove view或者控件之类等。自定义啊自定义,相对于Android 空间的自定义,)所以采用下面的方法来实现:在tableview向上滚动的时候,tag为0的cell将不再显示;然后我们把tag为0的cell移动到tag为9的cell下面,重新设置相关的属性,然后将tag为1的cell移动到tag为0的cell下面……依此类推。这也就是所谓的“可重用”。

但是此时被移动的tag为0的cell的一些属性还是保持不变的(包括之前添加的subView),因此就会出现一些无厘头的bug(看了这么多,到这里是不是松了口气? )。

         接下来我们就要使用多种的方法来干掉这个重用机制:          

         (1):  

                      UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

                      if (cell != nil) {

                                [cell release];  //怎么样?? 换了位置的Cell嚣张不了了吧....

                             }


        (2): //和(1)的方法本质一样,略显啰嗦。

UITableViewCell *cell =[tableView dequeueReusableCellWithIdentifier:CellIdentifier];

if (cell == nil) {

cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefaultreuseIdentifier:CellIdentifier]autorelease];

}

       NSArray*subviews = [[NSArrayalloc]initWithArray:cell.contentView.subviews];

for (UIView *subview in subviews) {

[subviewremoveFromSuperview];

}

[subviews release];


           //customer 


                      return cell;

  }

(3)://丫的,组别都不一样,看你怎么重用。

           NSString *CellIdentifier = [NSString stringWithFormat:@"cell%d",indexPath.row];
    
           UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
          if (cell == nil) 

                {
                               ........
                 }
         else{
                        return cell;
               }            

    

虽然干掉了重构机制,但我还是时不时地会想念它,特别是在数据多的时候,使用重用机制会好对你的程序的内存使用和优化都有很重要的作用。 但是这样的话,如果想再cell上添加东西的话,重叠现象会很严重。好吧,鱼和熊掌捆绑销售啦啦!!使用xib给cell添加视图来添加视图吧......

具体步骤:

(1)新建一个基于UITableViewCell的类A和一个空白的xib。

(2)在A类中声明要添加的视图,例如IBOutlet UILabel *nameLabel,*timeLabel; ,注意:一要是使用  IBOutlet。

(3)将xib中的view删除,拖一个 UITableViewCell,然后将这个UITableViewCell的类改为基于A。再把相应的视图添在UITableViewCell上,并且与A类内定义的变量进行连接。这样准备工作就完成了。

(4)使用方法:

                 A* cell = (A*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];


                 if (cell == nil) {


                         NSArray* nib = [[NSBundle mainBundle] loadNibNamed:@"VideoCell" owner:self options:nil];


                         cell = [nib objectAtIndex:0];


                 }

           然后只需在下面改变cell相应视图的属性就可以了。

补充:在使用地图MKMapView一会使用到重用机制,如果想要在MKPinAnnotationView添加视图的话,最好放弃那个重用机制,要不然效果会乱七八糟的(估计还有更好的处理方法)~~

    什么,你不喜欢用Xib?好吧,这个老外写的UITableView的代码,估计会合你的口味,实现方式如下:

    1、cell中的释放

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {

        cell = [[[UITableViewCell alloc] init....] autorelease];

        UITextField *field = ...;//初始化

        [cell addSubView:field];//添加

        [field release];//释放   

       }

2、通过遍历修改UILabel属性

  UITextField*field = nil;

   for(UIView *v in cell.contentView.subviews)

   {

         if([v isMemberOfClass:[UILabel class]])

             field = (UITextField *)v;

   }


             ......//接下来修改field的属性


这种方法不敢说好不好,但是给我们提供了一种解决的思路,看大家的喜好了```

嗯,不小心了弄了这么个“长篇巨幅”,到处借鉴同时混加自己的东西,如有雷同,算我抄你的。


推荐阅读
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • JVM 学习总结(三)——对象存活判定算法的两种实现
    本文介绍了垃圾收集器在回收堆内存前确定对象存活的两种算法:引用计数算法和可达性分析算法。引用计数算法通过计数器判定对象是否存活,虽然简单高效,但无法解决循环引用的问题;可达性分析算法通过判断对象是否可达来确定存活对象,是主流的Java虚拟机内存管理算法。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • Oracle10g备份导入的方法及注意事项
    本文介绍了使用Oracle10g进行备份导入的方法及相关注意事项,同时还介绍了2019年独角兽企业重金招聘Python工程师的标准。内容包括导出exp命令、删用户、创建数据库、授权等操作,以及导入imp命令的使用。详细介绍了导入时的参数设置,如full、ignore、buffer、commit、feedback等。转载来源于https://my.oschina.net/u/1767754/blog/377593。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • Asp.net Mvc Framework 七 (Filter及其执行顺序) 的应用示例
    本文介绍了在Asp.net Mvc中应用Filter功能进行登录判断、用户权限控制、输出缓存、防盗链、防蜘蛛、本地化设置等操作的示例,并解释了Filter的执行顺序。通过示例代码,详细说明了如何使用Filter来实现这些功能。 ... [详细]
author-avatar
平凡2188
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有