您可能不知道的iOS性能技巧(來自前Apple工程師)

您可能不知道的iOS性能技巧(來自前Apple工程師)

原地址:iOS Performance tips you probably didn't know (from an ex-Apple engineer)

? ios
? macos
? 可可
? 開發(fā)
? 性能

如果您想了解有關(guān)Cocoa開發(fā)和引導(dǎo)軟件業(yè)務(wù)的最新文章焕刮,請在Twitter上關(guān)注我或注冊郵件列表*同规。

作為開發(fā)人員,良好的性能對于使我們的用戶感到驚喜和喜悅是無價的。iOS用戶具有很高的標(biāo)準(zhǔn)偏竟,如果您的應(yīng)用程序呆滯或在內(nèi)存壓力下崩潰,他們將停止使用該應(yīng)用程序癣诱,或者更糟糕的是匿沛,您的評論將很糟糕。

在過去的6年中须板,我在Apple從事Cocoa框架和第一方應(yīng)用程序的開發(fā)工作碰镜。我曾經(jīng)從事SpotlightiCloud习瑰,應(yīng)用程序擴(kuò)展的工作绪颖,最近從事過Files的工作

我注意到有一種低垂的結(jié)果甜奄,您可以在20%的時間內(nèi)獲得80%的性能提升柠横。

這是一份性能提示清單,希望能給您帶來最大的收益:

1)UILabel成本超出您的想象

UILabel

一個UILabel在野外

我們很容易認(rèn)為標(biāo)簽在內(nèi)存使用方面是輕量級的课兄。最后牍氛,它們只顯示文本。UILabels實際上存儲為位圖烟阐,這很容易消耗兆字節(jié)的內(nèi)存搬俊。

值得慶幸的是,該UILabel實現(xiàn)很聰明,并且僅消耗其所需的資源:

  • 如果您的標(biāo)簽是單色的悠抹,UILabel將選擇CALayerContentsFormatkCAContentsFormatGray8Uint(每像素1個字節(jié))珠月,而非單色的標(biāo)簽(例如,顯示“參加聚會的時間”或彩色NSAttributedString)則需要使用kCAContentsFormatRGBA8Uint(每像素4個字節(jié))楔敌。

單色標(biāo)簽最多消耗width * height * contentsScale^2 * (1 byte per pixel)字節(jié)啤挎,而非單色標(biāo)簽最多消耗4倍width * height * contentsScale^2 * (4 bytes per pixel)

例如卵凑,在iPhone 11 Pro Max上庆聘,大小414 * 100點標(biāo)簽最多可消耗:

  • 414 * 100 * 3^2 * 1 = **372.6kB** (單色)
  • 414 * 100 * 3^2 * 4 = **~1.49MB** (非單色)

編輯:

在與UIKit工程師在Twitter上討論之后,我要提請注意勺卢。

確保始終先進(jìn)行測量伙判,如果性能問題確實是由標(biāo)簽引起的內(nèi)存壓力,請僅考慮以下更改黑忱。

從UIKit的@Inferis中

就目前的情況而言:假設(shè)將來對UILabel的更新可以優(yōu)化其(重新)使用后備存儲的方式宴抚,那么您的優(yōu)化現(xiàn)在會使事情(可能很多)變得更糟。

當(dāng)這些單元格進(jìn)入重用隊列時甫煞,一種常見的反模式是使UITableView/UICollectionView單元格標(biāo)簽填充文本內(nèi)容菇曲。一旦單元被回收,標(biāo)簽的文本值很可能會有所不同抚吠,因此存儲它們是浪費的常潮。

要釋放潛在的兆字節(jié)內(nèi)存:

  • text如果將標(biāo)簽設(shè)置為隱藏并且僅偶爾顯示,則取消標(biāo)簽楷力。
  • 如果標(biāo)簽text顯示在UITableView/UICollectionView單元格中喊式,則取消標(biāo)簽:
tableView(_:didEndDisplaying:forRowAt:)
collectionView(_:didEndDisplaying:forItemAt:)

2)始終從串行隊列開始,并且僅將并發(fā)隊列用作最后的選擇

常見的反模式是將不會影響UI的塊從主隊列分配到全局并發(fā)隊列之一萧朝。

例如:

func textDidChange(_ notification: Notification) {
    let text = myTextView.text
    myLabel.text = text
    DispatchQueue.global(qos: .utility).async {
        self.processText(text)
    }
}

如果我們暫停我們的申請:

螺紋爆炸

CDGCD為我們提交的每個塊創(chuàng)建了一個線程

當(dāng)您將dispatch_async一個塊放入并發(fā)隊列時岔留,GCD會嘗試在其線程池中找到一個空閑線程來運行該塊。如果找不到空閑線程剪勿,則必須為工作項創(chuàng)建一個新線程贸诚》酵ィ快速將塊分配到并發(fā)隊列可能導(dǎo)致快速創(chuàng)建新線程厕吉。

請記住:

  • 創(chuàng)建線程不是免費的械念。如果您要提交的工作塊很型分臁(<1毫秒),則在切換執(zhí)行上下文龄减,CPU周期和內(nèi)存弄臟方面創(chuàng)建新線程會很浪費项钮。
  • GCD會很樂意繼續(xù)為您創(chuàng)建線程,可能導(dǎo)致線程爆炸

通常烁巫,您應(yīng)該始終從數(shù)量有限的串行隊列開始署隘,每個串行隊列代表應(yīng)用程序的子組件(數(shù)據(jù)庫隊列,文本處理隊列等)亚隙。對于具有自己的串行分派隊列的較小對象磁餐,請使用來定位子組件隊列之一dispatch_set_target_queue

僅當(dāng)遇到瓶頸可以通過其他并發(fā)解決時阿弃,才使用您自己創(chuàng)建的并發(fā)隊列(不使用dispatch_get_global_queue)诊霹,并考慮使用dispatch_apply

關(guān)于的注釋dispatch_get_global_queue

您從中獲得的并發(fā)隊列dispatch_get_global_queue 不利于將QoS信息轉(zhuǎn)發(fā)到系統(tǒng)渣淳,因此應(yīng)避免脾还。

一個報價由libdispatch”皮埃爾Habouzit:

dispatch_get_global_queue() 實際上,這是調(diào)度API提供的最糟糕的事情之一入愧,因為盡管在運行時做出了所有最大的努力鄙漏,但是在運行時沒有足夠的有關(guān)您的操作/參與者/…的信息來了解您的意圖并對其進(jìn)行優(yōu)化。 棺蛛。

有關(guān)libdispatch效率提示的更詳細(xì)概述泥张,請查看此出色的匯編

3)可能沒有看起來那么糟糕

因此鞠值,您嘗試了盡可能多地優(yōu)化內(nèi)存使用率媚创,但是即使那樣,使用您的應(yīng)用程序一段時間后彤恶,內(nèi)存使用率仍然很高钞钙。

不用擔(dān)心,某些系統(tǒng)組件只有在收到內(nèi)存警告時才會釋放內(nèi)存声离。

例如芒炼,在低內(nèi)存情況下,UICollectionView-didReceiveMemoryWarning(從iOS 13開始)做出反應(yīng)术徊,從內(nèi)存中清除其重用隊列本刽。

模擬內(nèi)存警告:

  • 在iOS模擬器中,使用Simulate Memory Warning菜單項赠涮。
  • 在測試設(shè)備上子寓,調(diào)用私有API(請勿與此一起提交到App Store):
[[UIApplication sharedApplication] performSelector:@selector(_performMemoryWarning)];

4)避免dispatch_semaphore_t用于等待異步工作

這是一個常見的反模式:

let sem = DispatchSemaphore(value: 0)
makeAsyncCall {
    sem.signal()
}
sem.wait()

問題在于,優(yōu)先級信息不會傳播到將由其makeAsyncCall完成工作的其他線程/進(jìn)程笋除,并且可能導(dǎo)致優(yōu)先級倒置

  • 假設(shè)makeAsyncCall從主隊列進(jìn)行調(diào)用會將工作負(fù)載分派到QoS的數(shù)據(jù)庫隊列中QOS_CLASS_UTILITY斜友。
  • QOS_CLASS_USER_INITIATED由于來自主隊列的makeAsyncCall調(diào)用dispatch_async,DB隊列的QoS將得到提高垃它。
  • 用信號量阻塞主隊列意味著它被困在等待正在運行的工作QOS_CLASS_USER_INITIATED(低于主隊列的工作QOS_CLASS_USER_INTERACTIVE)鲜屏,因此優(yōu)先級反轉(zhuǎn)烹看。

附注XPC

如果您已經(jīng)使用過XPC(在macOS上,或者您正在使用NSFileProviderService)洛史,并且想要進(jìn)行同步調(diào)用惯殊,請避免使用信號量,而是使用以下命令將消息發(fā)送到同步代理:

- [NSXPCConnection synchronousRemoteObjectProxyWithErrorHandler:].

5)不要使用UIView標(biāo)簽

這是一種不好的做法也殖,并表明有代碼異味靠胜。這也不利于性能。

我最近使用過代碼毕源,一旦點擊一個視圖浪漠,便會根據(jù)其標(biāo)簽值更改其子視圖的顏色。

UIKit使用來實現(xiàn)標(biāo)簽objc_get/setAssociatedObject()霎褐,這意味著每次設(shè)置或獲取標(biāo)簽時址愿,您都在進(jìn)行字典查找,這可能會在熱循環(huán)中顯示在Instruments中:

[圖片上傳失敗...(image-58e2b0-1606097151280)]

<figcaption class="image-caption" style="box-sizing: inherit; font-style: normal; display: inherit; text-align: center; font-size: 14.4px; color: rgb(86, 86, 86);">-[UIView tag] 處理觸摸事件時要花費寶貴的毫秒冻璃。</figcaption>

編輯:充其量是微優(yōu)化响谓。我的收獲是:1)令人驚訝的-[UIView tag]是基于關(guān)聯(lián)的對象,而2)僅在性能敏感的代碼中大量使用它才有任何影響省艳。

離別的想法

希望您今天閱讀這些提示后能學(xué)到新的知識娘纷。與往常一樣,請確保在進(jìn)行性能調(diào)整之前先進(jìn)行測量跋炕。

有問題嗎赖晶?有更多性能提示要分享嗎?在評論中讓我知道辐烂!

插頭

您可以在這里查看我整潔的Mac實用程序遏插。

編輯

  • 感謝Paul HudsonUICollectionView/中使用時,糾正了使標(biāo)簽內(nèi)容無效的位置UITableView纠修。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末胳嘲,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子扣草,更是在濱河造成了極大的恐慌了牛,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辰妙,死亡現(xiàn)場離奇詭異鹰祸,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)上岗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門福荸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蕴坪,“玉大人肴掷,你說我怎么就攤上這事敬锐。” “怎么了呆瞻?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵台夺,是天一觀的道長。 經(jīng)常有香客問我痴脾,道長颤介,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任赞赖,我火速辦了婚禮滚朵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘前域。我一直安慰自己辕近,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布匿垄。 她就那樣靜靜地躺著移宅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪椿疗。 梳的紋絲不亂的頭發(fā)上漏峰,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天,我揣著相機(jī)與錄音届榄,去河邊找鬼浅乔。 笑死,一個胖子當(dāng)著我的面吹牛铝条,可吹牛的內(nèi)容都是我干的童擎。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼攻晒,長吁一口氣:“原來是場噩夢啊……” “哼顾复!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鲁捏,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤芯砸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后给梅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體假丧,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年动羽,在試婚紗的時候發(fā)現(xiàn)自己被綠了包帚。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡运吓,死狀恐怖渴邦,靈堂內(nèi)的尸體忽然破棺而出疯趟,到底是詐尸還是另有隱情,我是刑警寧澤谋梭,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布信峻,位于F島的核電站,受9級特大地震影響瓮床,放射性物質(zhì)發(fā)生泄漏盹舞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一隘庄、第九天 我趴在偏房一處隱蔽的房頂上張望踢步。 院中可真熱鬧,春花似錦丑掺、人聲如沸贾虽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蓬豁。三九已至,卻和暖如春菇肃,著一層夾襖步出監(jiān)牢的瞬間地粪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工琐谤, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留蟆技,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓斗忌,卻偏偏與公主長得像质礼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子织阳,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容