UITableView的原理——探究及重新实现代码
转自简书,原文地址,本文主要探讨一些特殊细节,像视图重用这类最基本的原理可在源码里查看。
先前重新实现了一个list容器视图,由于Apple没有开源,在此分享过程中探索到的UITableView一些细节。MPTableView: A list view like UITableView, more fast, more features.
1·捉摸不定的contentOffset
UISrollview在滑动的时候,要获取其不断变化的contentOffset值,可通过其协议来获取也可以在其layoutSubviews里面获得,而后者所获取到的offset值会来得频繁很多——当快速滑动的时候,scrollView的协议回调次数远远低于layoutSubviews调用次数,也即contentOffset的获取次数更少,那样一旦需要根据contentOffset来做某些精确的工作的话,则效果会更差。
即使选择最容易被调用的layoutSubviews,其调用次数也并非都是线性变化的,layoutSubviews被回调的次数是有限(n次1s)的,所以一旦急速滑动,并不会逐像素回调,而是总的滑动距离内调用一定次数,最后每次获得的contentOffset也将呈跳跃状。
2·关于UIView的视图层次
发现在addSubview之后通过insertSubview: AtIndex:来设置子视图的层级后,一旦重新修改子视图的frame则这个index将失效。而后面发现了通过view.layer.zPosition的提升则可以令view在其父视图中一直处于高层级或者低层级。
这个是在实现plain模式下tableview的section header/footer停浮时候,需要解决的问题。因为section视图很多是比cell早加入显示区域的(header),那么当滚动到需要header浮在cell的头上时(遮住),则会出现cell遮住header的情况。一开始选择了zPosition,但是发现这个就破坏了视图的原始状态,后面直接bringToFront实现plain的section悬浮。
3·update相关
多路insert/delete/reload操作。
(1)最好把某种操作的IndexSet/Array全部整合成一个数组,这样最多就只有3个数组了
(2)这3种操作中reload是优先进行的,至于insert和delete,UIKit是让delete先进行,再进行insert,在这里面,reload其实就是做了先delete后insert操作。
(3)经常可以看见某些app进行reload某个cell来进行扩张其内容(cell的height变大)的效果,如果这个cell底部有分割线或者是其他内容的话,那么在这个扩张cell的过程中,动画效果是这个cell下面的cell往下移动,cell的高度被扩充,但是那个分割线却没有移动的效果而是直接出现在了最底部。这是由于我们通常选择了None的动画方式来进行reload,而None的动画方式其实就是单纯的视图hidden.
(4)willInsertCell这个协议在insert动画之前是会被回调的,如果在insert操作里面选择了None的动画枚举,那么兴许可以通过这个协议做点自定义效果动画。这个不知道apple是否提供了这个机制。
4·UIScrollView的layoutSubviews调用时机
UIScrollView先进行setContentSize再进行addSubview操作,会执行layoutSubviews多次,而如果把setContentSize操作放到最后,那么只会执行一次layoutSubviews
5·UITableView的cell重用限制
UITableView并未对重用cell的数量做限制,在测试过程中,被划出屏幕外的所有cell都没被销毁。这个测试过程中,将cell按顺序用height递增,即每个cell的高度越来越大,那么将会导致一个情况,越是滑动到后面,显示区域里面的cell会越少(相比之前滑动经过的区域来说),导致了进入重用的cell数量递增(因为前面一屏显示的cell数量会更多),那么这些等待重用的cell虽然数量很大也不会被释放掉的。
6·实用的hidden。
UITableView对滑出屏幕的cell都是进行hidden的而非remove,在设计cell重用缓存的时候,发现一旦频繁的进行cell的remove操作会比使用hidden的cpu使用率高上10%(在iPhone5上面),而UITableView好像早前版本是remove现在也改为hidden了。
在实际开发中,如果不是得销毁UIView,那么hidden是比直接remove来得快。另外设置hidden为YES会触发一次其子视图的removeFromSuperView操作。
7·xib和纯frame设置的问题
xib加载的视图再用frame进行设置,接着如果改变父视图的frame会导致其height变为0,那么就是因为xib加载的这个视图的默认autoresizingMask有height相关的,置为none即可。这个问题之前很困扰,一直没有发现这个猫腻,当然,只有在前面提到的这个特定条件下(手动改变视图frame,而其上面的子视图又是通过xib加载的)才会出现的。
8·cell相关。
(1)dequeueReusableCellWithIdentifier:identifier这个函数不调用并不能帮助你实现cell的不重用,仅需要在进行初始化的时候把reuseIdentifier赋值为nil即可,在这里initWithFrame创建的cell应该就是默认reuseIdentifier为nil了,因为UITableViewCell的NS_DESIGNATED_INITIALIZER不在initWithFrame。
(2)UITableViewCell被点选的触发操作,其实并非在cell内部实现手势点击,而是通过在UITableView里面通过获得当前点击的位置,来判断点击的是哪一个cell,接着调用cell的setSelected:animated函数的。这样做应该也是为了避免耦合吧。
(3)UITableViewCell的选中高亮。当选中cell的时候,你会发现cell上面的所有视图,都变成了clearColor来让cell的高亮背景色显示出来,而当取消cell高亮的时候又会变回去,那么这个问题出现了。这些子视图的背景色被动了!那么原本的背景色又被记录在哪里呢,需要复原的时候又能跑出来。
在这里有2个思路:
·继承UIColor来写一个类,提供一个属性,受记录的颜色,当setBackgroundColor的时候碰到的是这个class,就用他记录的颜色来设置颜色。每次要将所有子视图都设置为clearColor的时候,就将这些子视图的背景色都用这个类记录下来。
·用一个容器存储这些视图对应的背景色,按键值对应,这里的key则选用每个UIView的内存地址。
·UITableView使用了一个UIView的私有api来解决这个问题:-[UIView _descendent:willMoveFromSuperview:toSuperview:]。
很重要的一点是,当cell上面的willRemoveSubview被触发时,如果当前cell呈被选中高亮状态,他就会帮这个要被remove的子视图回复原有的背景色。
(4)UITableViewCell的layoutSubviews。一般来讲cell上面的那些默认元素,titleLabel和删除按钮左右滑动的,很多时候是没有用的。而一旦在UITableViewCell子类的layoutSubviews里面有super layoutSubviews,也会照顾到这些默认视图,实测中也会使cpu的占用率下滑几个%。
MPTableView对UITableView的改进:
·提供了方法- (void)reloadDataAsyncWithCompletion:(void(^)(void))completion来进行异步的加载cell高度,选择了gcd的全局队列去计算和缓存高度完了再主线程刷新tableview——在这个过程中,如果tableview先前还有内容显示,那么在这个异步计算过程中,仍然可以滑动浏览原本的内容。
·修复了update动画,特别针对UITableView的plain模式下,进行多路的insert/delete/reload操作之后,section的header/footer都会有很奇怪的运动轨迹(这个情况也发生在Mac OX下的NSTable组件)。同时提供了insert/delete等的自定义动画方法。
·可以选择强制在reload的时候,继续重用之前产生的cell重用对象。UITableView的每次reload都会清除所有数据,可是很多时候,其实cell都是跟先前的一样,这样每次reload,无疑是多了删除旧的cell和创建新的过程——而实际上可以使用旧的。
·多路的update操作支持,可以同时/延迟开启多个update事务,并且设定不同时间、动画状态。
·同样的子视图样式下,cpu的占用率一定会比UITableView来得低。在同样的update操作下面,cpu的占用率也更低。还有cell拖动模式下也是。
·以及其他的一些改进和新增api,可以在更低系统版本下使用新的api。
UITableView的原理——探究及重新实现代码的更多相关文章
- [原] KVM 虚拟化原理探究(3)— CPU 虚拟化
KVM 虚拟化原理探究(3)- CPU 虚拟化 标签(空格分隔): KVM [TOC] CPU 虚拟化简介 上一篇文章笼统的介绍了一个虚拟机的诞生过程,从demo中也可以看到,运行一个虚拟机再也不需要 ...
- [原] KVM 虚拟化原理探究(2)— QEMU启动过程
KVM 虚拟化原理探究- QEMU启动过程 标签(空格分隔): KVM [TOC] 虚拟机启动过程 第一步,获取到kvm句柄 kvmfd = open("/dev/kvm", O_ ...
- KVO-基本使用方法-底层原理探究-自定义KVO-对容器类的监听
书读百变,其义自见! 将KVO形式以代码实现呈现,通俗易懂,更容易掌握 :GitHub -链接如果失效请自动搜索:https://github.com/henusjj/KVO_base 代码中有详 ...
- go--->共享内存和通信两种并发模式原理探究
共享内存和通信两种并发模式原理探究 并发理解 人类发明计算机编程的本质目的是为了什么呢?毫无疑问是为了解决人类社会中的各种负责业务场景问题.ok,有了这个出发点,那么想象一下,比如你既可以一心一意只做 ...
- ThreadPoolExcutor 原理探究
概论 线程池(英语:thread pool):一种线程使用模式.线程过多会带来调度开销,进而影响缓存局部性和整体性能.而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务.这避免了在处理短时间 ...
- [原] KVM 虚拟化原理探究(1)— overview
KVM 虚拟化原理探究- overview 标签(空格分隔): KVM 写在前面的话 本文不介绍kvm和qemu的基本安装操作,希望读者具有一定的KVM实践经验.同时希望借此系列博客,能够对KVM底层 ...
- [原] KVM 虚拟化原理探究 —— 目录
KVM 虚拟化原理探究 -- 目录 标签(空格分隔): KVM KVM 虚拟化原理探究(1)- overview KVM 虚拟化原理探究(2)- QEMU启动过程 KVM 虚拟化原理探究(3)- CP ...
- [原] KVM 虚拟化原理探究(6)— 块设备IO虚拟化
KVM 虚拟化原理探究(6)- 块设备IO虚拟化 标签(空格分隔): KVM [toc] 块设备IO虚拟化简介 上一篇文章讲到了网络IO虚拟化,作为另外一个重要的虚拟化资源,块设备IO的虚拟化也是同样 ...
- [原] KVM 虚拟化原理探究(5)— 网络IO虚拟化
KVM 虚拟化原理探究(5)- 网络IO虚拟化 标签(空格分隔): KVM IO 虚拟化简介 前面的文章介绍了KVM的启动过程,CPU虚拟化,内存虚拟化原理.作为一个完整的风诺依曼计算机系统,必然有输 ...
- [原] KVM 虚拟化原理探究(4)— 内存虚拟化
KVM 虚拟化原理探究(4)- 内存虚拟化 标签(空格分隔): KVM 内存虚拟化简介 前一章介绍了CPU虚拟化的内容,这一章介绍一下KVM的内存虚拟化原理.可以说内存是除了CPU外最重要的组件,Gu ...
随机推荐
- java8 Lambda 测试示例
import com.google.gson.Gson; import org.junit.Test; import java.util.Arrays; import java.util.IntSum ...
- uniapp iphone 6s获取时间戳失败
上代码 时间格式:2023-10-11 00:00:00 var ms = new Date(item.content.goodsList[0].end_time).getTime() 使用iphon ...
- Sealos 5.0 正式发布,云本应该是操作系统
把所有资源抽象成一个整体,一切皆应用,这才是云应该有的样子. 2018 年 8 月 15 日 Sealos 提交了第一行代码. 随后开源社区以每年翻倍的速度高速增长. 2022 年我们正式创业,经历一 ...
- hive第二课:Hive3.1.2分区与排序以及分桶(内置函数)
Hive3.1.2分区与排序(内置函数) 1.Hive分区(十分重要!!) 分区的目的:避免全表扫描,加快查询速度! 在大数据中,最常见的一种思想就是分治,我们可以把大的文件切割划分成一个个的小的文件 ...
- UG 2406 python 二次开发环境配置
UG 2406 python 二次开发环境配置 项目地址 https://gitee.com/unm001/nx2406.git 安装python 安装 python 3.10.11 D:\prog\ ...
- React中的Ref
React中ref是一个对象,它有一个current属性,可以对这个属性进行操作,用于获取DOM元素和保存变化的值.什么是保存变化的值?就是在组件中,你想保存与组件渲染无关的值,就是JSX中用不到的或 ...
- 如何合理开发Java接口(安全性,可重复调用,稳定性,追溯性)
一.接口开发规范 签名:对外提供的接口要做签名认证,认证不通过的请求不允许访问接口.提供服务. 加密:敏感数据在网络传输过程中应该加密. IP白名单:限制请求的IP,增加IP白名单,一般在网关层处理. ...
- mac svn管理工具
App Store中搜索snailsvn 分付费(98元)和免费试用
- 解决方案 | AutoCAD 版本+版本号+受支持的 .NET SDK版本+.NET Framework版本
关于 Managed .NET 兼容性 Managed .NET 应用程序通常与扩展基于 AutoCAD 的产品的行为和功能的公司和第三方应用程序关联. 在移植到最新版本后,并非所有 .NET 应用程 ...
- windows安装PHP的redis
一定要先看vc版本和位 配置php的redis扩展 以php7.3 nts版为例,不同的php版本对应不通的redis扩展:下载扩展文件:https://windows.php.net/downloa ...