昨天,接到一份“人人影视“面试的邀请,人人影视的HR说我内推网上的简历太简单了,问我有木有更详细的简历,随后我发了一份我精心制作的电子简历给她。刚开始我以为这家公司是创新工场旗下的一家子公司,所以还比较感兴趣,因为创新工场的创始人是李开复。在高中我就读过李开复的自传,并且一直奉为我滴偶像。但是通过了解,这家公司只是有创新工场的投资,而非它的子公司,属于创业型的小公司,规模在20人以下,而我想去的是大公司,所以没什么太大的兴趣了。不久,那边的技术面试官就打电话过来了,一点铺垫也没有,直接上来就是问技术问题,我都没反应过来,整个人还是懵的。他问了我一个问题:怎么优化ListView?刚开始我没明白ListView是什么?我说我没听清楚,然后他又重复了一遍,我是不懂,我说我不懂ListView是什么。然后他说怎么优化列表?这回我懂了,我是做IOS开发的,不太懂安卓开发,列表控件在安卓里面叫ListView,而在我们IOS开发中我们称之为TableView。我感觉这个面试官一点都不专业,再加上我对他们公司不太感兴趣,所以没有怎么认真的回答他,只是淡淡的说了一句,用下拉刷新,其实我没有回答到点子上,我应该说用列表重用机制的。回想一下,我还是不应该这么随便应付的,所以我决定还是要写篇博客详细总结写这个问题,这个问题还是比较好滴。
以前,写列表的时候,根本就没有列表重用概念,因为数据量很少,所以没用列表重用机制也没发现什么问题。但是在实际的项目开发中,当列表数据很多的时候,如果不用列表重用机制,就会导致列表滑动卡顿或者是列表数据重复等问题。这是因为
(1) iOS设备的内存有限,如果用UITableView显示成千上万条数据,就需要成千上万 个UITableViewCell对象的话,那将会耗尽iOS设备的内存。要解决该问题,需要重用UITableViewCell对象
(2)重⽤原理:当滚动列表时,部分UITableViewCell会移出窗口,UITableView会将窗口外的UITableViewCell放入一个对象池中,等待重用。当UITableView要求dataSource返回 UITableViewCell时,dataSource会先查看这个对象池,如果池中有未使用的 UITableViewCell,dataSource则会用新的数据来配置这个UITableViewCell,然后返回给 UITableView,重新显示到窗口中,从而避免创建新对象 。这样可以让创建的cell的数量维持在很低的水平,如果一个窗口中只能显示5个cell,那么cell重用之后,只需要创建6个cell就够了。
一、不用列表重用机制
#import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; UITableView *tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 20, self.view.frame.size.width, self.view.frame.size.height-20) style:UITableViewStylePlain]; tableView.delegate = self; tableView.dataSource = self; [self.view addSubview:tableView]; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 20; } - (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *identifier = @"CELL"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; if (cell == nil) { cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; } UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(5, 5, 110, 70)]; imageView.image = [UIImage imageNamed:@"image.jpg"]; [cell addSubview:imageView]; UILabel *labelTitle = [[UILabel alloc]initWithFrame:CGRectMake(120, 0, 200, 25)]; labelTitle.text = [NSString stringWithFormat:@"这是列表的第%ld条测试数据",(NSInteger)indexPath.row]; [cell addSubview:labelTitle]; return cell; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return 80; } @end
不用列表重用机制,列表有多少条数据就会相应的创建多少个cell, 如果有一万条数据就会创建一万个cell,对内存而言会很耗内存,本身手机的内存又比较小,所以有时候真机测试的时候会出现列表滑动很卡的现象。另外,会导致列表界面数据重复或者混乱的现象,如下图所示:
#import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; UITableView *tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 20, self.view.frame.size.width, self.view.frame.size.height-20) style:UITableViewStylePlain]; tableView.delegate = self; tableView.dataSource = self; [self.view addSubview:tableView]; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 20; } - (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *identifier = @"CELL"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; if (cell == nil) { cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(5, 5, 110, 70)]; [imageView setTag:100]; [cell.contentView addSubview:imageView]; UILabel *labelTitle = [[UILabel alloc]initWithFrame:CGRectMake(120, 0, 250, 25)]; [labelTitle setTag:101]; [cell.contentView addSubview:labelTitle]; } UIImageView *imageView = (UIImageView*)[cell viewWithTag:100]; imageView.image = [UIImage imageNamed:@"image.jpg"]; UILabel *labelTitle = (UILabel*)[cell viewWithTag:101]; labelTitle.text = [NSString stringWithFormat:@"这是列表的第%ld条测试数据",(NSInteger)indexPath.row]; return cell; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return 80; } @end