日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

        歡迎訪問 生活随笔!

        生活随笔

        當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

        编程问答

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

        發(fā)布時間:2025/7/14 编程问答 32 豆豆
        生活随笔 收集整理的這篇文章主要介紹了 [性能优化]UITableView性能优化的一点感悟及计算UILabel高度的新方法 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

        前言?

        在使用過程中發(fā)現(xiàn),我們App的首頁在快速滑動時會出現(xiàn)掉幀,以及在上拉加載更多時會抖動,因為首頁模塊是以前的同事寫的,很多代碼已不適應(yīng)當前的需求,所以產(chǎn)生了優(yōu)化的想法,優(yōu)化主要分為以下幾個方面:

        • 緩存cell高度(發(fā)現(xiàn)了一種計算Label高度的新方法)
        • 優(yōu)化cellForRow方法
        • 圖片加載優(yōu)化
        • 禁止tableView預(yù)估高度
        • 刪除無用數(shù)據(jù)處理邏輯

        緩存cell高度

        在Feed流中,UITableViewCell的高度通常是變化的,需要根據(jù)返回的數(shù)據(jù)中的cell類型以及l(fā)abel的文字長度來計算高度,而在UITableView中func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell

        是一個高頻調(diào)用的方法,為了減少CPU的計算,盡可能減少掉幀,所以需要將高度進行緩存,在我們的項目中,首頁的數(shù)據(jù)是這樣一個操作流程后臺返回的JSON->FeedListModel->FeedsModel->各種cell的ViewModel(例如小圖片的cell對應(yīng)的model-SmallImageCellViewModel,大圖片的cell對應(yīng)的-BigImageCellViewModel)FeedListModel主要是包含了一些頁碼信息和FeedsModel數(shù)組FeedsModel儲存著后臺返回的cell所需的信息BigImageCellViewModel是cell對FeedsModel進行處理后得到cell所需的信息

        優(yōu)化以前,我們的高度是通過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屬性,所以一開始我也是把計算屬性改成存儲屬性了,但是還是很耗時(后來才發(fā)現(xiàn)是因為高度是存在BigImageCellViewModel中的,而每次數(shù)據(jù)更新后,由于業(yè)務(wù)需要會對當前的列表數(shù)據(jù)重新遍歷處理,生成新的BigImageCellViewModel,新的BigImageCellViewModel的高度自然是每次需要計算),在使用instruments分析時發(fā)現(xiàn),在加載數(shù)據(jù)時,1s內(nèi)有20%的時候是用于計算每個cell的高度,因為計算cell高度時需要根據(jù)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

        因為即便是同一個字符串,字體大小一樣,字體不同時,高度會不一定一樣,這個方法會根據(jù)字符串和對應(yīng)字體進行繪制計算后得到的高度,而且這個操作是在主線程進行的,所以會導(dǎo)致掉幀,然后我就網(wǎng)上查閱資料怎么優(yōu)化這個方法,網(wǎng)上這方面的資料比較少因為這個方法的耗時本身是可接受范圍以內(nèi)的,只是我們的height沒有真正緩存上導(dǎo)致這個方法測試時特別耗時,這種思路是思路一在子線程中調(diào)用這個方法,然后對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賦值上,但是在實際測試中發(fā)現(xiàn),大部分cell取的是我們預(yù)設(shè)的高度默認值,這樣在下次reloadData時,cell的高度會取算出來的值,然后會導(dǎo)致tableView的contentSize變化,視圖抖動然后我就自己思考,其實我們的標題并不復(fù)雜,大部分是中文,其他是數(shù)字,標點符號,字母,然后我就測試了一下在UIFont.boldSystemFont(ofSize: 16)下,中文,數(shù)字,標點符號,字母的大小,然后測試發(fā)現(xiàn)中文 15pt 數(shù)字是8pt左右,主要的一些標點符號16pt 小寫字母大概8pt,大寫自貿(mào)銀11pt,就想能不能通過對標題字符串進行遍歷,判斷字符的類型來計算標題的總寬度,之后再將總寬度除以標題的最大寬度得到行數(shù),然后計算得出cell高度,代碼如下:

        思路二 計算Label高度的新方法 通過遍歷字符串來計算高度

        -(CGFloat)calculateTotalWidthInBold16 {

        CGFloat totalWidth = 0;

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

        unichar character = [self characterAtIndex:i];

        //中 占15pt 數(shù)字 占7 英文 a 8.2 A 10.1 B 10.6 , ? 16.6pt

        if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:character]) {//數(shù)字

        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,但是數(shù)字,小寫字母,大寫字母的長度不是固定的,所以如果需要做到非常準確,需要對每個數(shù)字,字母在這個字體下的長度進行測試。

        在緩存高度的情況下,與boundingRect方法相比,這個方法也能夠提高計算速度,只是收益不那么明顯

        優(yōu)化cellForRow方法

        因為tableView的cellForRow方法也是一個調(diào)用頻率特別高的方法,所以應(yīng)該避免在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的分割線,但是我們是預(yù)加載的,其實很少能看到最后一個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

        圖片加載優(yōu)化

        主要使用charles進行抓包,看項目有沒有加載比較大的圖片,我們項目首頁的三張圖片的資訊使用的是大圖,一張圖片長達4M,所以我改成小圖了

        禁止tableView預(yù)估高度

        因為tableView會根據(jù)estimatedRowHeight*行數(shù)來計算contentSize,并且在滑動時進行修正,所以會發(fā)生抖動,所以可以通過以下代碼,禁用預(yù)估高度,因為iOS11以后預(yù)估高度的值不為0,所以需要顯式賦值為0

        tableView.estimatedRowHeight = 0

        tableView.estimatedSectionHeaderHeight = 0

        tableView.estimatedSectionFooterHeight = 0

        刪除無用數(shù)據(jù)處理邏輯

        主要注釋了代碼中沒有用的數(shù)據(jù)處理邏輯

        總結(jié)

        以上其實只是針對我們項目一些比較基本的優(yōu)化的地方,當然還有很多地方可以進行優(yōu)化,例如將cell中view的布局進行緩存,減少不必要的計算,還有將一些Label通過異步渲染的方式繪制在cell中,減少view的層級,將一部分渲染的工作放在子線程中,但是這樣會對我們的項目改動過大,所以暫時沒有采用


        PS: 最近加了一些iOS開發(fā)相關(guān)的QQ群和微信群,但是感覺都比較水,里面對于技術(shù)的討論比較少,所以自己建了一個iOS開發(fā)進階討論群,歡迎對技術(shù)有熱情的同學(xué)掃碼加入,加入以后你可以得到:?

        1.技術(shù)方案的討論,會有在大廠工作的高級開發(fā)工程師盡可能抽出時間給大家解答問題 ?

        2.每周定期會寫一些文章,并且轉(zhuǎn)發(fā)到群里,大家一起討論,也鼓勵加入的同學(xué)積極得寫技術(shù)文章,提升自己的技術(shù)?

        3.如果有想進大廠的同學(xué),里面的高級開發(fā)工程師也可以給大家內(nèi)推,并且針對性得給出一些面試建議?

        群已經(jīng)滿100人了,想要加群的小伙伴們可以掃碼加這個微信,備注:“加群+昵稱”,拉你進群,謝謝了 !





        總結(jié)

        以上是生活随笔為你收集整理的[性能优化]UITableView性能优化的一点感悟及计算UILabel高度的新方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

        如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。