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

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

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

ios
macos
可可
開發
性能

如果您想了解有關Cocoa開發和引導軟件業務的最新文章,請在Twitter上關注我或註冊郵件列表*。

作爲開發人員,良好的性能對於使我們的用戶感到驚喜和喜悅是無價的。iOS用戶具有很高的標準,如果您的應用程序呆滯或在內存壓力下崩潰,他們將停止使用該應用程序,或者更糟糕的是,您的評論將很糟糕。

在過去的6年中,我在Apple從事Cocoa框架和第一方應用程序的開發工作。我曾經從事SpotlightiCloud應用程序擴展的工作,最近從事過Files的工作

我注意到有一種低垂的結果,您可以在20%的時間內獲得80%的性能提升

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

1)UILabel成本超出您的想象

一個UILabel在野外

我們很容易認爲標籤在內存使用方面是輕量級的。最後,它們只顯示文本。UILabels實際上存儲爲位圖,這很容易消耗兆字節的內存。

值得慶幸的是,該UILabel實現很聰明,並且僅消耗其所需的資源:

  • 如果您的標籤是單色的UILabel將選擇CALayerContentsFormatkCAContentsFormatGray8Uint(每像素1個字節),而非單色的標籤(例如,顯示“參加聚會的時間”或彩色NSAttributedString)則需要使用kCAContentsFormatRGBA8Uint(每像素4個字節)。

單色標籤最多消耗width * height * contentsScale^2 * (1 byte per pixel)字節,而非單色標籤最多消耗4倍width * height * contentsScale^2 * (4 bytes per pixel)

例如,在iPhone 11 Pro Max上,大小414 * 100點標籤最多可消耗:

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

編輯:

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

確保始終先進行測量,如果性能問題確實是由標籤引起的內存壓力,請僅考慮以下更改。

從UIKit的@Inferis中

就目前的情況而言:假設將來對UILabel的更新可以優化其(重新)使用後備存儲的方式,那麼您的優化現在會使事情(可能很多)變得更糟。

當這些單元格進入重用隊列時,一種常見的反模式是使UITableView/UICollectionView單元格標籤填充文本內容。一旦單元被回收,標籤的文本值很可能會有所不同,因此存儲它們是浪費的。

要釋放潛在的兆字節內存:

  • text如果將標籤設置爲隱藏並且僅偶爾顯示,則取消標籤。
  • 如果標籤text顯示在UITableView/UICollectionView單元格中,則取消標籤:
tableView(_:didEndDisplaying:forRowAt:)
collectionView(_:didEndDisplaying:forItemAt:)

2)始終從串行隊列開始,並且僅將併發隊列用作最後的選擇

常見的反模式是將不會影響UI的塊從主隊列分配到全局併發隊列之一。

例如:

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

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

CDGCD爲我們提交的每個塊創建了一個線程

當您將dispatch_async一個塊放入併發隊列時,GCD會嘗試在其線程池中找到一個空閒線程來運行該塊。如果找不到空閒線程,則必須爲工作項創建一個新線程。快速將塊分配到併發隊列可能導致快速創建新線程。

請記住:

  • 創建線程不是免費的。如果您要提交的工作塊很小(<1毫秒),則在切換執行上下文,CPU週期和內存弄髒方面創建新線程會很浪費。
  • GCD會很樂意繼續爲您創建線程,可能導致線程爆炸

通常,您應該始終從數量有限的串行隊列開始,每個串行隊列代表應用程序的子組件(數據庫隊列,文本處理隊列等)。對於具有自己的串行分派隊列的較小對象,請使用來定位子組件隊列之一dispatch_set_target_queue

僅當遇到瓶頸可以通過其他併發解決時,才使用您自己創建的併發隊列(不使用dispatch_get_global_queue),並考慮使用dispatch_apply

關於的註釋dispatch_get_global_queue

您從中獲得的併發隊列dispatch_get_global_queue 不利於將QoS信息轉發到系統,因此應避免

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

dispatch_get_global_queue() 實際上,這是調度API提供的最糟糕的事情之一,因爲儘管在運行時做出了所有最大的努力,但是在運行時沒有足夠的有關您的操作/參與者/…的信息來了解您的意圖並對其進行優化。 。

有關libdispatch效率提示的更詳細概述,請查看此出色的彙編

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

因此,您嘗試了儘可能多地優化內存使用率,但是即使那樣,使用您的應用程序一段時間後,內存使用率仍然很高。

不用擔心,某些系統組件只有在收到內存警告時纔會釋放內存

例如,在低內存情況下,UICollectionView-didReceiveMemoryWarning(從iOS 13開始)做出反應,從內存中清除其重用隊列。

模擬內存警告:

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

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

這是一個常見的反模式:

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

問題在於,優先級信息不會傳播到將由其makeAsyncCall完成工作的其他線程/進程,並且可能導致優先級倒置

  • 假設makeAsyncCall從主隊列進行調用會將工作負載分派到QoS的數據庫隊列中QOS_CLASS_UTILITY
  • QOS_CLASS_USER_INITIATED由於來自主隊列的makeAsyncCall調用dispatch_async,DB隊列的QoS將得到提高。
  • 用信號量阻塞主隊列意味着它被困在等待正在運行的工作QOS_CLASS_USER_INITIATED(低於主隊列的工作QOS_CLASS_USER_INTERACTIVE),因此優先級反轉。

附註XPC

如果您已經使用過XPC(在macOS上,或者您正在使用NSFileProviderService),並且想要進行同步調用,請避免使用信號量,而是使用以下命令將消息發送到同步代理:

- [NSXPCConnection synchronousRemoteObjectProxyWithErrorHandler:].

5)不要使用UIView標籤

這是一種不好的做法,並表明有代碼異味。這也不利於性能。

我最近使用過代碼,一旦點擊一個視圖,便會根據其標籤值更改其子視圖的顏色。

UIKit使用來實現標籤objc_get/setAssociatedObject(),這意味着每次設置或獲取標籤時,您都在進行字典查找,這可能會在熱循環中顯示在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>

編輯:充其量是微優化。我的收穫是:1)令人驚訝的-[UIView tag]是基於關聯的對象,而2)僅在性能敏感的代碼中大量使用它纔有任何影響。

離別的想法

希望您今天閱讀這些提示後能學到新的知識。與往常一樣,請確保在進行性能調整之前先進行測量

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

插頭

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

編輯

  • 感謝Paul HudsonUICollectionView/中使用時,糾正了使標籤內容無效的位置UITableView
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章