AG亚游集团 >iOS开发

[性能优化]UITableView性能优化的一点感悟及计算UILabel高度的新方法

2019-01-10 10:20 编辑: Gboy 分类:iOS开发 来源:Not_Found

前言 

在使用过程中发现,我们App的首页在快速滑动时会出现掉帧,以及在上拉加载更多时会抖动,因为首页模块是以前的同事写的,很多代码已不适应当前的需求,所以产生了优化的想法,优化主要分为以下几个方面:

  • 缓存cell高度(发现了一种计算Label高度的新方法)

  • 优化cellForRow方法

  • 图片加载优化

  • 禁止tableView预估高度

  • 删除无用数据处理逻辑

缓存cell高度

在Feed流中,UITableViewCell的高度通常是变化的,需要根据返回的数据中的cell类型以及label的文字长度来计算高度,而在UITableView中func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell

是一个高频调用的方法,为了减少CPU的计算,尽可能减少掉帧,所以需要将高度进行缓存,在我们的项目中,首页的数据是这样一个操作流程后台返回的JSON->FeedListModel->FeedsModel->各种cell的ViewModel(例如小图片的cell对应的model-SmallImageCellViewModel,大图片的cell对应的-BigImageCellViewModel)FeedListModel主要是包含了一些页码信息和FeedsModel数组FeedsModel储存着后台返回的cell所需的信息BigImageCellViewModel是cell对FeedsModel进行处理后得到cell所需的信息

优化以前,我们的高度是通过BigImageCellViewModel中计算属性height去获取的

var height: CGFloat {

      guard let title = title else {

return ((UIScreen.mainWidth - 30) * 9)/16.0 + 62

}

let constraintRect = CGSize(width: UIScreen.mainWidth - 30, height: 38.5)

let attributes = [NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 16)]

let rect = title.boundingRect(with: constraintRect,

options: .usesLineFragmentOrigin,

attributes: attributes,

context: nil)

if let type = itemType, type == ItemType.sohuVideo {

return ((UIScreen.mainWidth - 30) * 9)/16.0 + rect.height + 62

}

return ((UIScreen.mainWidth - 30) * 9)/16.0 + rect.height + 62

}

这样的话每次取值时,会需要通过计算然后返回height属性,所以一开始我也是把计算属性改成存储属性了,但是还是很耗时(后来才发现是因为高度是存在BigImageCellViewModel中的,而每次数据更新后,由于业务需要会对当前的列表数据重新遍历处理,生成新的BigImageCellViewModel,新的BigImageCellViewModel的高度自然是每次需要计算),在使用instruments分析时发现,在加载数据时,1s内有20%的时候是用于计算每个cell的高度,因为计算cell高度时需要根据model.title确定cell中的标题Label显示几行,从而确定Label的高度,进而算出cell的高度,而计算Label高度一般都是使用这个方法,

@available(iOS 7.0, *)

open func boundingRect(with size: CGSize, options: NSStringDrawingOptions = [], attributes: [NSAttributedString.Key : Any]? = nil, context: NSStringDrawingContext?) -> CGRect

因为即便是同一个字符串,字体大小一样,字体不同时,高度会不一定一样,这个方法会根据字符串和对应字体进行绘制计算后得到的高度,而且这个操作是在主线程进行的,所以会导致掉帧,然后我就网上查阅资料怎么优化这个方法,网上这方面的资料比较少因为这个方法的耗时本身是可接受范围以内的,只是我们的height没有真正缓存上导致这个方法测试时特别耗时,这种思路是思路一在子线程中调用这个方法,然后对height进行赋值,类似于这样:

思路一 异步计算Label高度

var height:CGFloat = 70

let queue = DispatchQueue.global()

queue.async {

let labelRect = title.boundingRect(with: constraintRect,

options: .usesLineFragmentOrigin,

attributes: attributes,

context: nil)

height = labelRect.height + 50

}

就是通过先给height赋一个概率最大的值,然后通过异步计算后,得到一个准确值,再给height赋值上,但是在实际测试中发现,大部分cell取的是我们预设的高度默认值,这样在下次reloadData时,cell的高度会取算出来的值,然后会导致tableView的contentSize变化,视图抖动然后我就自己思考,其实我们的标题并不复杂,大部分是中文,其他是数字,标点符号,字母,然后我就测试了一下在UIFont.boldSystemFont(ofSize: 16)下,中文,数字,标点符号,字母的大小,然后测试发现中文 15pt 数字是8pt左右,主要的一些标点符号16pt 小写字母大概8pt,大写自贸银11pt,就想能不能通过对标题字符串进行遍历,判断字符的类型来计算标题的总宽度,之后再将总宽度除以标题的最大宽度得到行数,然后计算得出cell高度,代码如下:

思路二 计算Label高度的新方法 通过遍历字符串来计算高度

-(CGFloat)calculateTotalWidthInBold16 {

CGFloat totalWidth = 0;

for (int i = 0; i < self.length; i++) {

unichar character  = [self characterAtIndex:i];

/中 占15pt 数字 占7 英文 a 8.2 A 10.1 B 10.6 , ? 16.6pt

if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:character]) {/数字

totalWidth += 8;

} else if ([[NSCharacterSet lowercaseLetterCharacterSet] characterIsMember:character]) {/小写字母

totalWidth += 10;

} else if ([[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:character]) {/大写字母

totalWidth += 12;

} else if ([[NSCharacterSet punctuationCharacterSet] characterIsMember:character]) {/标点符号

totalWidth += 17;

} else if (character >= 0x4E00 && character <= 0x9FA5) {

totalWidth += 15;

} else {

totalWidth += 15;

}

}

return totalWidth + 5;

}

在不缓存高度的情况下,这个方法能够很快得计算出高度,让tableview达到平均55帧以上的帧率,但是缺点是需要对使用的字体下进行测试,在UIFont.boldSystemFont(ofSize: 16)字体下,中文是固定的15pt,但是数字,小写字母,大写字母的长度不是固定的,所以如果需要做到非常准确,需要对每个数字,字母在这个字体下的长度进行测试。

在缓存高度的情况下,与boundingRect方法相比,这个方法也能够提高计算速度,只是收益不那么明显

优化cellForRow方法

因为tableView的cellForRow方法也是一个调用频率特别高的方法,所以应该避免在cellForRow对cell进行约束修改,frame变化等操作,

open func cellForRow(at indexPath: IndexPath) -> UITableViewCell? /returns nil if cell is not visible or index path is out of range

主要是把这部分代码注释掉了,这部分操作主要是为了隐藏最后一个cell的分割线,但是我们是预加载的,其实很少能看到最后一个cell的底部,所以其实没有必要

default: /feed流

let cellViewModel = viewModel.viewModels.value[indexPath.row]

let cell = configFeedCell(tableView: tableView, cellViewModel: cellViewModel, indexPath: indexPath)

/           cell.saHorizontalSpace = (15, 15)

/           if viewModel.isInfrontOfFeedSpacAble(indexPath: indexPath) {

/           cell.saSeparaptorLineStyle = .bottom

/           } else if cellViewModel as? FeedSpacAble != nil {

/               cell.saSeparaptorLineStyle = .bottom

/           } else {

/               cell.saSeparaptorLineStyle = .none

/           }

return cell

图片加载优化

主要使用charles进行抓包,看项目有没有加载比较大的图片,我们项目首页的三张图片的资讯使用的是大图,一张图片长达4M,所以我改成小图了

禁止tableView预估高度

因为tableView会根据estimatedRowHeight*行数来计算contentSize,并且在滑动时进行修正,所以会发生抖动,所以可以通过以下代码,禁用预估高度,因为iOS11以后预估高度的值不为0,所以需要显式赋值为0

tableView.estimatedRowHeight = 0

tableView.estimatedSectionHeaderHeight = 0

tableView.estimatedSectionFooterHeight = 0

删除无用数据处理逻辑

主要注释了代码中没有用的数据处理逻辑

总结

以上其实只是针对我们项目一些比较基本的优化的地方,当然还有很多地方可以进行优化,例如将cell中view的布局进行缓存,减少不必要的计算,还有将一些Label通过异步渲染的方式绘制在cell中,减少view的层级,将一部分渲染的工作放在子线程中,但是这样会对我们的项目改动过大,所以暂时没有采用


作者:Not_Found
链接:/juejin0686im/post/5bf608fff265da614e2bb799


搜索CocoaChina微信公众号:CocoaChina
微信扫一扫
订阅每日移动开发及APP推广热点资讯
公众号:
CocoaChina
我要投稿   收藏文章
上一篇:AG亚游集团iOS开发笔记— Xcode、UITabbar、特殊机型问题分析
我来说两句
发表评论
您还没有登录!请AG亚游集团登录注册
所有评论(0

综合评论

相关帖子

sina weixin mail 回到顶部
男子借2000元被法院判还22000元 只因这样写借条 耀才证券:特朗普招数难测 港股乏明确方向续争持 高盛:特斯拉未来两年最多需融资100亿美元 曹阳:正视与鲁能的差距 盼米克尔在世界杯走得远 塔利斯卡激发恒大“鲶鱼效应” 眼中只有乘胜追击 “出行教父”李斌和他的“理想国” 棋葩说:小鱼儿不输花木兰 柯洁应摆正姿态 鲁媒盘点中国足球三个死循环:技不如人却找他因 索萨:三线作战力争开门红 束总这样的老板很少见 梅县外中甲还藏大黑马 中超级核心搭档恒大旧将 董明珠看好的银隆半年巨变:订单下降、多地停工 人社部又有好消息了 事业单位职工必看
中超-吴兴涵补时绝杀蒿俊闵破门 鲁能2-1胜泰达 拜仁主帅:希望蒂亚戈留下来 但我无法做出保证 “酷暑奥运”引担忧 东京都知事:部分比赛将提早 火箭跨过NBA头号连胜终结者!也错过个夺冠铁律 阿富汗总统:愿与塔利班组织展开无条件和谈 男篮世界排名:中国第28下降4位 澳洲排亚太第1 女子每月染发一次 十年后得了这种病 继送货入室后 亚马逊推出送货入车服务 7次扑救+首位U23首发门将 国安还藏这么牛一门将 印度男女队双双惨败 印度网友批评:成为了笑话 [新浪彩票]足彩18068期投注策略:里昂主胜可搏 马克龙:不赞成美欧再次展开大型贸易协定谈判
拼多多的假货里,藏着最真实的中国 巴布亚新几内亚再遭6.7级余震 至少18人死亡 AETOS艾拓思:政府停摆危机升温 美元续跌非美慢涨 陈希参加青海代表团审议:坚决拥护宪法修正案草案 腾讯周三公布全年业绩 券商估多赚逾4成 伤仲永?王军辉曾为卡帅屡立奇功 进最佳新人候选 工会福利费大量结余之后 这个地方的做法令人深思 奇牛国际:加拿大4月CPI表现疲软 美加震荡上扬 特朗普:美未从朝鲜方面接到取消新加坡峰会的通知 中国食品品牌排行榜 加盟什么店最赚钱 文科生可以报哪些专业 手机挂机软件一小时5元 AG亚游集团