转自:http://www.2cto.com/kf/201207/144337.htmlhtml
今天在看iphone开发秘籍的时候,遇到这个问题,就仔细的深刻了一下,经过测试,获取了一些自认为还不错的结论,但愿对你们在cell复用方面遇到的一些问题会有所帮助。编程
本篇文章只讲原理,对于若是对cell作界面,不深刻讲述。鉴于个人表达能力有限,可能会有我本身清楚,可是却说不清楚的地方,若有问题,留言给我。iphone
UITableView在界面的编程用的甚多,iphone开发也三月有余了,每次用到cellForRowAtIndexPath的委托方法的时候,都是直接copy代码,本身略加一些界面的修改,对于cell的标示符都是static NSString* identifier = @"cell";而后调用dequeueReusableCellWithIdentifier方法获取cell,若是cell为空,再调用[[[UITableViewCellalloc]initWithStyle方法新建立一个,根本没有考虑过更深一些的东西。为了讲解清楚,现放上一段代码,代码copy自iphone开发秘籍,本人为了讲解,略加修改。如下全部讲解均依照此代码进行,所以,若是您但愿可以透彻的了解cell的复用机制,建议实际运行如下,跟着讲解,查看效果。代码只有tableVIew的委托方法,所以您须要本身建立工程,把这些委托方法加进去。ide
上文一共四个委托方法,代表tableView一个section,有32行,高度为58.关于tableView的高度那个委托方法的返回的高度值,建议最好本身运行程序查看如下,最终达到一页的界面显示8个cell,第8个cell显示一半也行,可是不能显示9和7个cell。我这里之因此为58,因为(480 - 44)/ 58 为7.5 的样子,44为navigationBar的高度,480为屏幕的高度。总之,须要达到的效果是一页显示7个多的cell。还有那个icon.png须要本身准备了,要把它显示出来。是否是很麻烦呀?没办法,谁让咱们在得到知识呢,知识老是须要点功夫的。测试
[cpp]
- (NSInteger)numberOfSectionsInTableView:(UITableView *)aTableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section
{
return 32;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 58;
}
- (UITableViewCell *)tableView:(UITableView *)tView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCellStyle style;
NSString *cellType;
switch (indexPath.row % 4)
{
case 0:
style = UITableViewCellStyleDefault;
cellType = @"Default Style";
//有标题没有正文(没有细节文字)。可选的图片
break;
case 1:
style = UITableViewCellStyleSubtitle;
cellType = @"Subtitle Style";
//标题和正文方式,上下排布。可选的图片
break;
case 2:
style = UITableViewCellStyleValue1;
cellType = @"Value1 Style";
//左边文字左对齐,右边文字右对齐。可选的图片
break;
case 3:
style = UITableViewCellStyleValue2;
cellType = @"Value2 Style";
//左边文字右对齐,蓝色;右边文字左对齐,黑色。没有图片
break;
}
static int i = 0;
UITableViewCell *cell = [tView dequeueReusableCellWithIdentifier:cellType];
if (!cell)
{
cell = [[[UITableViewCell alloc] initWithStyle:style reuseIdentifier:cellType] autorelease];
++i;
NSLog(@"cell created %d times", i);
}
if (indexPath.row > 3)
cell.imageView.image = [UIImage imageNamed:@"icon.png"];
cell.textLabel.text = cellType;
cell.detailTextLabel.text = @"Subtitle text";
return cell;
} 日志
首先讲解一下复用队列:htm
复用队列的元素增长:只有在cell被滑动出界面的时候,此cell才会被加入到复用队列中。每次在建立cell的时候,程序会首先经过调用dequeueReusableCellWithIdentifier:cellType方法,到复用队列中去寻找标示符为“cellType”的cell,若是找不到,返回nil,而后程序去经过调用[[[UITableViewCell alloc] initWithStyle:style reuseIdentifier:cellType] autorelease]来建立标示符为“cellType”的cell。队列
先运行一次程序,不要滑动cell,查看打印日志,会有打印"cell create 1 times",,,"cell create 8 times",一共有8个日志,代表cell建立了8次,这个日志打印是在cellForRowAtIndexPath中的建立cell的时候打印的。而后慢慢向下(向下的意思,其实是手向上滑动,让界面显示下一个cell,向上与之相反)滑动cell,到显示第九个cell的时候,会看到打印一条日志"cell create 9 times",而后继续慢慢向下滑动,会有"cell create 10 times",直到滑动到第12个cell之后,cell被建立了12个以后,之后再怎么滑动,cell都不会被建立了。也就是说本tableView要完整的工做,一共建立了12个cell。事件
开始解释缘由了:图片
第一页的界面一共须要展现8个cell,故而cell须要建立8次,每个cell负责本身的数据显示。此8个建立之后,复用队列依然为空(由于你此时尚未滑动cell呢,复用队列的元素不会增长)。而后在向下滑动显示出第9个cell的时候,还会调用cellForRowAtIndexPath方法,在此方法中,它首先到可复用队列中去找,因为此时队列为空,它建立了一个cell,打印日志,同时当第1个cell滑动出界面以外,第一个cell进入到复用队列中,队列中有一个元素,此元素的表示符为@"Default Style"。而后继续向下滑动cell,开始显示第10个cell,它一样到复用队列中去找,第10个cell的标示符为@"Subtitle Style",可是队列中惟一的cell的标示符为@"Default Style",根据标示符寻找,没有找到,故而再次建立一个新的cell,同时将滑动出界面的第2个cell进入复用队列。此时复用队列有两个元素,标示符为@"Default Style",@"Subtitle Style"。一样的道理,滑动到第11个cell的时候,第3个cell入队,第12个cell的时候,第4个cell入队。此时复用队列的元素个数为四个,标示符分别为:@"Default Style",@"Subtitle Style",@"Value1 Style",@"Value2 Style".而后继续滑动cell,当滑动到第13个cell的时候,它的标示符为@"Default Style",它到复用队列中去找,能够找到。故而,这个cell就不会被建立了,同理,再次向下滑动,如下的全部cell均可以根据标示符找到对应的cell,不会有被建立的。在向下滑动的过程当中,滑动出去的cell会被入队,不过只入队建立了的cell,也就是说最终队列中会有12个cell。对于每次取对应标示符的元素,到底取的是哪个?采用的什么策略?这个我不知道,我经过打印查看的是在向下滑动的过程当中,一直顺着队列找,队列是一个循环队列。向上滑动的时候,逆着队列向上找,因为是循环队列,一直在转圈。
若是你仔细看的话,你会发现第一个cell原本没有图片,为何一划下去再次滑动(若是滑动的距离远,一次搞定,若是滑动的距离近,多上下滑动几回)上来又有图片了呢?这个就要思考一个:第一个cell在滑出界面又划入界面的时候,是从复用队列拿到的。复用队列有12个元素,第1,5,9还第一个cell有相同的标示符,第1个没有图片,第5个和第9个有图片。当用户复用的是第一个cell的时候,它是没有图片,当用户复用第5,9个cell的时候,它是有图片的。所以,当你上下滑动,查看第一个cell的时候,你会看到它一会有图片,一会没有图片。这个跟复用时候的队列查找规则有关。
实用篇:
说了那么多,全是关于原理的。如今说点实用的。若是你想在全部的cell中添加一个按钮,你是应该在if中添加,仍是应该在if以外添加呢?毫无疑问,应该在if中,若是你是在if的外面添加的,那会致使,你在向下滑动cell的过程当中,取出来的cell原本已经带有button了,而你还在addSubview,按钮愈来愈多。或者你能够采用在if外面添加,前提是每次先cell remove掉其全部的子视图。这样太消耗cpu,麻烦了。若是你想一行隔着一行有按钮和没有按钮,你该怎么作呢?稍微思考一下,这个但是两种风格的cell,故而在滑出界面进行重用的时候,它们应该属于不一样的标示符。因而你在建立cell的时候,应该去指定两种标示符,建立两种cell。固然,也许你聪明了,我是否是能够在if以外先remove掉cell的全部子视图,而后根据row % 2 == 0或者!=0 来进行addSubView:button吗?答案固然是确定的,可是这样仍是一样的问题,太消耗cpu了。日常咱们给每个cell添加了一个button,确定要添加事件的。对于不一样的button,响应不一样的事件?那么我是否是经过在if语句中给button设置它的tag标记(例如button.tag = indexpath.row)来实现呢?哈哈,你应该足够聪明了吧,固然不行。你能够这样想,if语句是用来建立的,它只被执行了(例如上面的例子:12次),可是你可能有几百行的cell,固然你的tag也就只有12个了,明显不对应。像这样的,应该怎么处理呢?答案是:放在if的外面。你在if外面设置了tag标记,固然,在某一个具体的时间点上,仍然只有12个标记,可是这12个标记是可变的,例如当前界面显示第100-111号的cell,那么此时的button的tag就会是100-111了,仍然是12个按钮,可是它们会根据用户的滑动,进行不一样的tag切换,至关于拥有了不少个按钮。若是你没有被我说的话给弄晕,脑壳又足够清醒的话,你应该能够得出如下的结论:对于界面的定制,放在if中比较好,一个cell中只建立一次;对于数据的定制,放在if外面比较好,对于不一样的cell,表示不一样的内容,虽然只有12个cell,可是cell中存放的数据我能够任意的映射。若是你得出了这个结论,那么若是在加上textField,label等等,你应该能够轻松搞定。不只仅是表面上,更重要的是,你理解了原理,掌握了机制,万变都不怕,即便有新的需求,脑壳想一想,或者拿着这篇文章看看,但愿能给你一些启示。