一个问题引发的血案,以下是本侦探的探案过程的一部分:以下全部都是转载自别人的博客:http://blog.sina.com.cn/s/blog_9c3c519b01016aqu.html

转自:http://www.2cto.com/kf/201207/144337.html

今天在看iphone开发秘籍的时候,遇到这个问题,就仔细的深入了一下,通过测试,获取了一些自认为还不错的结论,希望对大家在cell复用方面遇到的一些问题会有所帮助。

本篇文章只讲原理,对于如果对cell做界面,不深入讲述。鉴于我的表达能力有限,可能会有我自己清楚,但是却说不清楚的地方,如有问题,留言给我。

UITableView在界面的编程用的甚多,iphone开发也三月有余了,每次用到cellForRowAtIndexPath的委托方法的时候,都是直接copy代码,自己略加一些界面的修改,对于cell的标示符都是static NSString* identifier = @"cell";然后调用dequeueReusableCellWithIdentifier方法获取cell,如果cell为空,再调用[[[UITableViewCellalloc]initWithStyle方法新创建一个,根本没有考虑过更深一些的东西。为了讲解清楚,现放上一段代码,代码copy自iphone开发秘籍,本人为了讲解,略加修改。以下所有讲解均依照此代码进行,因此,如果您希望能够透彻的了解cell的复用机制,建议实际运行以下,跟着讲解,查看效果。代码只有tableVIew的委托方法,因此您需要自己创建工程,把这些委托方法加进去。

上文一共四个委托方法,表明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需要自己准备了,要把它显示出来。是不是很麻烦呀?没办法,谁让我们在获得知识呢,知识总是需要点功夫的。//经鉴定这些都是废话,可忽略不计---SandyZhang 鉴定。

[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; 
}

//前方高能预警,请集中精力----SandyZhang 鉴定。
首先讲解一下复用队列:

复用队列的元素增加:只有在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的时候,你会看到它一会有图片,一会没有图片。这个跟复用时候的队列查找规则有关。

//看到事件篇瞬间感觉萌萌哒---SandyZhang 鉴定。
实用篇:

说了那么多,全是关于原理的。现在说点实用的。如果你想在所有的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等等,你应该可以轻松搞定。不仅仅是表面上,更重要的是,你理解了原理,掌握了机制,万变都不怕,即使有新的需求,脑袋想想,或者拿着这篇文章看看,希望能给你一些启示。

作者:lipeng08

//亲爱的小伙伴,你看懂了吗?(没有?再看一遍吧)

cell 的复用机制的更多相关文章

  1. cell的复用机制

    以下全部都是转载自别人的博客:http://blog.sina.com.cn/s/blog_9c3c519b01016aqu.html 转自:http://www.2cto.com/kf/201207 ...

  2. Cell的复用机制问题总结

    创建方式汇总,注册和不注册Cell注册的两种方式 1.tableView registerNib:(nullable UINib *) forCellReuseIdentifier:(nonnull ...

  3. tableView中cell的复用机制

    TableView的重用机制,为了做到显示和数据分离,IOS tableView的实现并且不是为每个数据项创建一个tableCell.而是只创建屏幕可显示最大个数的cell,然后重复使用这些cell, ...

  4. Tableview 优化Cell的复用机制01

    #import "ViewController.h" @interface ViewController ()<UITableViewDataSource> @end ...

  5. QF——UITableViewCell性能优化(视图复用机制)

    这几篇博客总结的不错: 点击进入 点击进入 总结起来方案一般有以下几种: 1.不使用透明视图: 2.减少视图的个数: 3.cell复用机制:(重点) 4.图片缓存: 5.网络请求使用非主线程. 6.预 ...

  6. Android学习笔记之ListView复用机制

    PS:满打满算,差不多三个月没写博客了...前一阵忙的不可开交...总算是可以抽出时间研究研究其他事情了... 学习内容: 1.ListView的复用机制 2.ViewHolder的概念 1.List ...

  7. iOS--cell的重用机制

    对于像我们这样的初学者来说,cell重用机制是很难理解的内容,所以我们不一定非得理解,会用就行. cell的重用机制:当我们使用tableView时,系统只会创建屏幕中显示的cell的个数+1,当ce ...

  8. 再谈select, iocp, epoll,kqueue及各种I/O复用机制

    原文:http://blog.csdn.net/shallwake/article/details/5265287 首先,介绍几种常见的I/O模型及其区别,如下: blocking I/O nonbl ...

  9. I/O复用机制概述

    导读 /O多路复用技术通过把多个I/O的阻塞复用到同一个select的阻塞上,从而使得系统在单线程的情况下可以同时处理多个客户端请求.与传统的多线程/多进程模型比,I/O多路复用的最大优势是系统开销小 ...

随机推荐

  1. SQLite剖析之设计与概念

    1.API 由两部分组成: 核心API(core API)和扩展API(extension API). 核心API的函数实现基本的数据库操作:连接数据库.处理SQL.遍历结果集.它也包括一些实用函数, ...

  2. [转]Hibernate延迟加载与opensessioninviewFilter

    原文地址:http://blog.csdn.net/a19881029/article/details/7916702 hibernate延迟加载: 一个person对应多个school,使用hibe ...

  3. C#-WinForm-Treeview-树状模型

    Treeview - 树状模型 利用递归添加数据 数据放入 treeView1.Nodes.Add() 中 public Form3() { InitializeComponent(); TreeNo ...

  4. Python3 基本数据类型

    Python中的变量不需要声明,每个变量使用前必须赋值,变量赋值后才会被创建,在Python中变量就是变量,它没有类型.我们所说的"类型"是变量所指的内存中对象的类型. 等号(=) ...

  5. WinNTSetup v3.8.7 正式版绿色增强版

    最强系统安装利器:WinNTSetup 现已更新至 v3.8.7 正式版!这次更新修复调整了诸多问题,新版非常好用接近完美!WinNTSetup 现在已经自带BCDBoot 选项,并且完全支持Wind ...

  6. c# WebClient Get Post 方法

    public string GetData(string url) { string data; using (var client = new WebClient()) { using (var s ...

  7. 【原】理解javascript中的闭包

    闭包在javascript来说是比较重要的概念,平时工作中也是用的比较多的一项技术.下来对其进行一个小小的总结 什么是闭包? 官方说法: 闭包是指有权访问另一个函数作用域中的变量的函数.创建闭包的常见 ...

  8. $this-->name

    如果要在模板中输出变量,必须在在控制器中把变量传递给模板,系统提供了assign方法对模板变量赋值,无论何种变量类型都统一使用assign赋值. $this->assign('name',$va ...

  9. Mac Pro 利用PHP导出SVN新增或修改过的文件

    先前在 Windows 操作系统下,习惯用 TortoiseSVN 导出新增或修改过的文件([相当实用]如何让TortoiseSVN导出新增或修改过的文件 ),最近换成了 Mac Pro 笔记本电脑, ...

  10. mongDB-- 3. 查询操作

    1. 准备工作 (1)启动mongo 进入mongo安装目录的bin/ 目录 , ./mongod (2)启动mongo客户端 ./mongo (3) 查看所有库 show dbs; (4) 切换到l ...