forkingdog关于UITableView优化的框架其实已经能够应用在一般的场景,且有蛮多的知识点供我们借鉴,借此站在巨人的肩膀上来分析一把。

至于UITableView的瓶颈在哪里,我相信网上随便一搜就能了解的大概,我这里顺便提供下信息点:

   //罪魁祸首 tableView:cellForRowAtIndexPath: tableView:heightForRowAtIndexPath: 
 
框架同样根据这两个痛点给出了解决方案:

高度计算

fd_heightForCellWithIdentifier:configuration方法
 - (CGFloat)fd_heightForCellWithIdentifier:(NSString *)identifier configuration:(void (^)(id cell))configuration {
if (!identifier) {
return ;
} UITableViewCell *templateLayoutCell = [self fd_templateCellForReuseIdentifier:identifier]; // Manually calls to ensure consistent behavior with actual cells. (that are displayed on screen)
[templateLayoutCell prepareForReuse]; // Customize and provide content for our template cell.
if (configuration) {
configuration(templateLayoutCell);
} return [self fd_systemFittingHeightForConfiguratedCell:templateLayoutCell];
}

这里先是通过调用fd_templateCellForReuseIdentifier:从dequeueReusableCellWithIdentifier取出之后,如果需要做一些额外的计算,比如说计算cell高度, 可以手动调用 prepareForReuse方法,以确保与实际cell(显示在屏幕上)行为一致。接着执行configuration参数对Cell内容进行配置。最后通过调用fd_systemFittingHeightForConfiguratedCell:方法计算实际的高度并返回。

fd_systemFittingHeightForConfiguratedCell方法
 - (CGFloat)fd_systemFittingHeightForConfiguratedCell:(UITableViewCell *)cell {
CGFloat contentViewWidth = CGRectGetWidth(self.frame); // If a cell has accessory view or system accessory type, its content view's width is smaller
// than cell's by some fixed values.
if (cell.accessoryView) {
contentViewWidth -= + CGRectGetWidth(cell.accessoryView.frame);
} else {
static const CGFloat systemAccessoryWidths[] = {
[UITableViewCellAccessoryNone] = ,
[UITableViewCellAccessoryDisclosureIndicator] = ,
[UITableViewCellAccessoryDetailDisclosureButton] = ,
[UITableViewCellAccessoryCheckmark] = ,
[UITableViewCellAccessoryDetailButton] =
};
contentViewWidth -= systemAccessoryWidths[cell.accessoryType];
} // If not using auto layout, you have to override "-sizeThatFits:" to provide a fitting size by yourself.
// This is the same height calculation passes used in iOS8 self-sizing cell's implementation.
//
// 1. Try "- systemLayoutSizeFittingSize:" first. (skip this step if 'fd_enforceFrameLayout' set to YES.)
// 2. Warning once if step 1 still returns 0 when using AutoLayout
// 3. Try "- sizeThatFits:" if step 1 returns 0
// 4. Use a valid height or default row height (44) if not exist one CGFloat fittingHeight = ; if (!cell.fd_enforceFrameLayout & contentViewWidth > ) {
// Add a hard width constraint to make dynamic content views (like labels) expand vertically instead
// of growing horizontally, in a flow-layout manner.
NSLayoutConstraint *widthFenceConstraint = [NSLayoutConstraint constraintWithItem:cell.contentView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:contentViewWidth];
[cell.contentView addConstraint:widthFenceConstraint]; // Auto layout engine does its math
fittingHeight = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
[cell.contentView removeConstraint:widthFenceConstraint]; [self fd_debugLog:[NSString stringWithFormat:@"calculate using system fitting size (AutoLayout) - %@", @(fittingHeight)]];
} if (fittingHeight == ) {
#if DEBUG
// Warn if using AutoLayout but get zero height.
if (cell.contentView.constraints.count > ) {
if (!objc_getAssociatedObject(self, _cmd)) {
NSLog(@"[FDTemplateLayoutCell] Warning once only: Cannot get a proper cell height (now 0) from '- systemFittingSize:'(AutoLayout). You should check how constraints are built in cell, making it into 'self-sizing' cell.");
objc_setAssociatedObject(self, _cmd, @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
#endif
// Try '- sizeThatFits:' for frame layout.
// Note: fitting height should not include separator view.
fittingHeight = [cell sizeThatFits:CGSizeMake(contentViewWidth, )].height; [self fd_debugLog:[NSString stringWithFormat:@"calculate using sizeThatFits - %@", @(fittingHeight)]];
} // Still zero height after all above.
if (fittingHeight == ) {
// Use default row height.
fittingHeight = ;
} // Add 1px extra space for separator line if needed, simulating default UITableViewCell.
if (self.separatorStyle != UITableViewCellSeparatorStyleNone) {
fittingHeight += 1.0 / [UIScreen mainScreen].scale;
} return fittingHeight;
}

这里作者考虑到了如果Cell使用了accessory view或者使用了系统的accessory type,需要减掉相应的宽度。接着判断如果使用了AutoLayout,则使用iOS 6提供的systemLayoutSizeFittingSize方法获取高度。如果高度为0,则尝试使用Frame Layout的方式,调用重写的sizeThatFits方法进行获取。如果还是为0,则给出默认高度并返回。

Cell重用

fd_templateCellForReuseIdentifier方法
 - (__kindof UITableViewCell *)fd_templateCellForReuseIdentifier:(NSString *)identifier {
NSAssert(identifier.length > , @"Expect a valid identifier - %@", identifier); NSMutableDictionary *templateCellsByIdentifiers = objc_getAssociatedObject(self, _cmd);
if (!templateCellsByIdentifiers) {
templateCellsByIdentifiers = @{}.mutableCopy;
objc_setAssociatedObject(self, _cmd, templateCellsByIdentifiers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
} UITableViewCell *templateCell = templateCellsByIdentifiers[identifier]; if (!templateCell) {
templateCell = [self dequeueReusableCellWithIdentifier:identifier];
NSAssert(templateCell != nil, @"Cell must be registered to table view for identifier - %@", identifier);
templateCell.fd_isTemplateLayoutCell = YES;
templateCell.contentView.translatesAutoresizingMaskIntoConstraints = NO;
templateCellsByIdentifiers[identifier] = templateCell;
[self fd_debugLog:[NSString stringWithFormat:@"layout cell created - %@", identifier]];
} return templateCell;
}

这里通过dequeueReusableCellWithIdentifier方法从队列中获取templateCell,并通过fd_isTemplateLayoutCell属性标识其只用来充当模板计算,并不真正进行呈现,最后通过关联对象的方式进行存取。

注意:这里通过dequeueReusableCellWithIdentifier进行获取,也就意味着你必须对指定的Identifier先进行注册,注册可以通过以下三中方法:

  .使用storyboard中的Cell原型 .使用registerNib:forCellReuseIdentifier: .使用registerClass:forCellReuseIdentifier: 
 
到这里最重要的几个方法已经讲完了,除此之外框架还针对获取的高度进行了缓存。缓存的方式分为两种 :
   .根据IndexPath进行缓存(fd_heightForCellWithIdentifier:cacheByIndexPath:configuration) .根据实体的唯一标识符进行缓存(fd_heightForCellWithIdentifier:cacheByKey:configuration) 

总结:

UITableView优化方案其实还有很多,不同的场景选用不同的方案,实现效果达到预期,这才是我么最终的目标。我这里简单介绍下其他的优化的细节:

  .复杂界面的时候,我们可以尝试异步手动进行绘制。 .针对超出屏幕的Cell进行预缓存 .存在大量图片的时候,只针对目标范围内的图片进行异步加载并缓存结果。 .设置Views/Layers为不透明。 
 

UITableView优化那点事的更多相关文章

  1. uitableview 优化

    1. http://www.cocoachina.com/ios/20150602/11968.html 最近在微博上看到一个很好的开源项目VVeboTableViewDemo,是关于如何优化UITa ...

  2. iOS UITableView优化

    一.Cell 复用 在可见的页面会重复绘制页面,每次刷新显示都会去创建新的 Cell,非常耗费性能.  解决方案:创建一个静态变量 reuseID,防止重复创建(提高性能),使用系统的缓存池功能. s ...

  3. UITableView优化技巧

    UITableView的简单认识 UITableView最核心的思想就是UITableViewCell的重用机制.简单的理解就是:UITableView只会创建一屏幕(或一屏幕多一点)的UITable ...

  4. [转] 详细整理:UITableView优化技巧

      原文:http://www.cocoachina.com/ios/20150602/11968.html   最近在微博上看到一个很好的开源项目VVeboTableViewDemo,是关于如何优化 ...

  5. UITableView优化方案

    1.UITableView的简单认识 > UITableView最核心的思想就是UITableViewCell的重用机制.简单的理解就是:UITableView只会创建一屏幕(或一屏幕多一点)的 ...

  6. UITableView 优化总结

    最近在微博上看到一个很好的开源项目VVeboTableViewDemo,是关于如何优化UITableView的.加上正好最近也在优化项目中的类似朋友圈功能这块,思考了很多关于UITableView的优 ...

  7. C#编译器优化那点事 c# 如果一个对象的值为null,那么它调用扩展方法时为甚么不报错 webAPI 控制器(Controller)太多怎么办? .NET MVC项目设置包含Areas中的页面为默认启动页 (五)Net Core使用静态文件 学习ASP.NET Core Razor 编程系列八——并发处理

    C#编译器优化那点事   使用C#编写程序,给最终用户的程序,是需要使用release配置的,而release配置和debug配置,有一个关键区别,就是release的编译器优化默认是启用的.优化代码 ...

  8. UITableView优化

    作为iOS开发,UITableView可能是平时我们打交道最多的UI控件之一,其重要性不言而喻. 关于TableView,我想最核心的就是UITableViewCell的重用机制了. 简单来说呢就是当 ...

  9. app 性能优化的那些事(二)

    来源:树下的老男孩 链接:http://www.jianshu.com/p/2a01e5e2141f 这次我们来说说iOS app中滑动的那些事.iOS为了提高滑动的流畅感,特意在滑动的时候将runl ...

随机推荐

  1. Erlang分布式在linux和windows上的注意事项

    没事做,就在两台机器间测试下Erlang分布式的例子,一个台是windowsXP,一台装的redHat9,没有详细的文档,自己摸索着搞成功了,记录下. 1.首先,分布式Erlang的实现提供了自有的安 ...

  2. ZOJ 2283 Challenge of Wisdom

    题意:在一个n * m的地图里,有p个宝藏,每次只能向横纵坐标增加的方向走,问最少走几次能把宝藏都拿走. 解法:按横坐标排序,纵坐标的一个不下降子序列就是一条合法路径,要求多少条不下降子序列可以覆盖所 ...

  3. Android自定义控件步骤总结

    在android开发中,系统提供给我们的控件不能满足我们的需求,所以我们往往会自定义一些控件,在自定义过程中遵循以下几个步骤: 一.定义一个Class继承于系统View类 二.在xml文件中配置lay ...

  4. POJ 1236 Network of Schools 有向图强连通分量

    参考这篇博客: http://blog.csdn.net/ascii991/article/details/7466278 #include <stdio.h> #include < ...

  5. bug描述注意点

    一个好的错误跟踪系统包括了错误的必要信息,如果做得不好,会造成迷惑,并误导读者.好的故障描述应该包括十个基本部分:标题.项目.所属模块.优先级.重要性.异常等级.可重复性.现象.操作过程和附件. ①标 ...

  6. #pragma alloc_text 与 ALLOC_PRAGMA

    百度标题中的两部分,可以找到很多文章,现将收集到的其中两篇整理如下: 转载链接:http://hi.baidu.com/billbeggar/item/c378e2ea39a5daeffa42bada ...

  7. STM32的can现场总线实验心得

    最近在搞stm32实验板的can现场总线实验,之前只是搞过STC51的串口通信,相比之下,发觉can总线都挺复杂的.开始时,知道自己是新手,只知道can总线跟串行通信,485通信,I2C通信一样都是用 ...

  8. wuzhicms 模块开发

    首先,模块开发需要了解五指cms的目录结构: 然后,我们需要新增加一个模块目录: 再app下面创建 如:content 下面包含文件: 前台文件的创建: 看下 index.php 的内容: <? ...

  9. sublime Text2.0.2注册码

    ----- BEGIN LICENSE ----- Andrew Weber Single User License EA7E-855605 813A03DD 5E4AD9E6 6C0EEB94 BC ...

  10. 深度学习 CNN CUDA 版本2

    作者:zhxfl 邮箱:zhxfl##mail.ustc.edu.cn 主页:http://www.cnblogs.com/zhxfl/p/4155236.html 第1个版本blog在这里:http ...