iOS面試題:簡述效能最佳化

SLPA發表於2020-12-10

在效能最佳化中一個最具參考價值的屬性是FPS:Frames Per Second,其實就是螢幕重新整理率,蘋果的iphone推薦的重新整理率是60Hz,也就是說GPU每秒鐘重新整理螢幕60次,這每重新整理一次就是一幀frame,FPS也就是每秒鐘重新整理多少幀畫面。靜止不變的頁面FPS值是0,這個值是沒有參考意義的,只有當頁面在執行動畫或者滑動的時候,FPS值才具有參考價值,FPS值的大小體現了頁面的流暢程度高低,當低於45的時候卡頓會比較明顯。
圖層混合:
每一個layer是一個紋理,所有的紋理都以某種方式堆疊在彼此的頂部。對於螢幕上的每一個畫素,GPU需要算出怎麼混合這些紋理來得到畫素RGB的值。

當Sa = 0.5時,RGB值為(0.5, 0, 0),可以看出,當兩個不是完全不透明的CALayer覆蓋在一起時,GPU大量做這種複合操作,隨著這中操作的越多,GPU越忙碌,效能肯定會受到影響。

公式:
R = S + D * ( 1 – Sa )

結果的顏色是源色彩(頂端紋理)+目標顏色(低一層的紋理)*(1-源顏色的透明度)。

當Sa = 1時,R = S,GPU將不會做任何合成,而是簡單從這個層複製,不需要考慮它下方的任何東西(因為都被它遮擋住了),這節省了GPU相當大的工作量。

一、入門級

1、用ARC管理記憶體

2、在正確的地方使用 reuseIdentifier

3、儘量把views設定為透明

4、避免過於龐大的XIB

5、不要阻塞主執行緒

6、在ImageViews中調整圖片大小。如果要在UIImageView中顯示一個來自bundle的圖片,你應保證圖片的大小和UIImageView的大小相同。在執行中縮放圖片是很耗費資源的,特別是UIImageView巢狀在UIScrollView中的情況下。如果圖片是從遠端服務載入的你不能控制圖片大小,比如在下載前調整到合適大小的話,你可以在下載完成後,最好是用background
thread,縮放一次,然後在UIImageView中使用縮放後的圖片。

7、選擇正確的Collection。

  • Arrays: 有序的一組值。使用index來lookup很快,使用value lookup很慢, 插入/刪除很慢。
  • Dictionaries: 儲存鍵值對。 用鍵來查詢比較快。
  • Sets: 無序的一組值。用值來查詢很快,插入/刪除很快。

8、開啟gzip壓縮。app可能大量依賴於伺服器資源,問題是我們的目標是移動裝置,因此你就不能指望網路狀況有多好。減小文件的一個方式就是在服務端和你的app中開啟gzip。這對於文字這種能有更高壓縮率的資料來說會有更顯著的效用。
iOS已經在NSURLConnection中預設支援了gzip壓縮,當然AFNetworking這些基於它的框架亦然。

二、中級

1、重用和延遲載入(lazy load) Views

  • 更多的view意味著更多的渲染,也就是更多的CPU和記憶體消耗,對於那種巢狀了很多view在UIScrollView裡邊的app更是如此。
  • 這裡我們用到的技巧就是模仿UITableView和UICollectionView的操作: 不要一次建立所有的subview,而是當需要時才建立,當它們完成了使命,把他們放進一個可重用的佇列中。這樣的話你就只需要在滾動發生時建立你的views,避免了不划算的記憶體分配。

2、Cache, Cache, 還是Cache!

  • 一個極好的原則就是,快取所需要的,也就是那些不大可能改變但是需要經常讀取的東西。
  • 我們能快取些什麼呢?一些選項是,遠端伺服器的響應,圖片,甚至計算結果,比如UITableView的行高。
  • NSCache和NSDictionary類似,不同的是系統回收記憶體的時候它會自動刪掉它的內容。
    3、權衡渲染方法.效能能還是要bundle保持合適的大小。

4、處理記憶體警告.移除對快取,圖片object和其他一些可以重建立的objects的strong references.

5、重用大開銷物件

6、一些objects的初始化很慢,比如NSDateFormatter和NSCalendar。然而,你又不可避免地需要使用它們,比如從JSON或者XML中解析資料。想要避免使用這個物件的瓶頸你就需要重用他們,可以透過新增屬性到你的class裡或者建立靜態變數來實現。

7、避免反覆處理資料.在伺服器端和客戶端使用相同的資料結構很重要。

8、選擇正確的資料格式.解析JSON會比XML更快一些,JSON也通常更小更便於傳輸。從iOS5起有了官方內建的JSON deserialization 就更加方便使用了。但是XML也有XML的好處,比如使用SAX 來解析XML就像解析本地檔案一樣,你不需像解析json一樣等到整個文件下載完成才開始解析。當你處理很大的資料的時候就會極大地減低記憶體消耗和增加效能。

9、正確設定背景圖片

  • 全屏背景圖,在view中新增一個UIImageView作為一個子View
  • 只是某個小的view的背景圖,你就需要用UIColor的colorWithPatternImage來做了,它會更快地渲染也不會花費很多記憶體:

10、減少使用Web特性。想要更高的效能你就要調整下你的HTML了。第一件要做的事就是儘可能移除不必要的javascript,避免使用過大的框架。能只用原生js就更好了。儘可能非同步載入例如使用者行為統計script這種不影響頁面表達的javascript。注意你使用的圖片,保證圖片的符合你使用的大小。

11、Shadow Path 。CoreAnimation不得不先在後臺得出你的圖形並加好陰影然後才渲染,這開銷是很大的。使用shadowPath的話就避免了這個問題。使用shadow path的話iOS就不必每次都計算如何渲染,它使用一個預先計算好的路徑。但問題是自己計算path的話可能在某些View中比較困難,且每當view的frame變化的時候你都需要去update shadow path.

12、最佳化Table View

  • 正確使用reuseIdentifier來重用cells
  • 儘量使所有的view opaque,包括cell自身
  • 避免漸變,圖片縮放,後臺選人
  • 快取行高
  • 如果cell內現實的內容來自web,使用非同步載入,快取請求結果
  • 使用shadowPath來畫陰影
  • 減少subviews的數量
  • 儘量不適用cellForRowAtIndexPath:,如果你需要用到它,只用-一次然後快取結果
  • 使用正確的資料結構來儲存資料
  • 使用rowHeight, sectionFooterHeight 和 sectionHeaderHeight來設定固定的高,不要請求delegate

13、選擇正確的資料儲存選項

  • NSUserDefaults的問題是什麼?雖然它很nice也很便捷,但是它只適用於小資料,比如一些簡單的布林型的設定選項,再大點你就要考慮其它方式了
  • XML這種結構化檔案呢?總體來說,你需要讀取整個檔案到記憶體裡去解析,這樣是很不經濟的。使用SAX又是一個很麻煩的事情。
  • NSCoding?不幸的是,它也需要讀寫檔案,所以也有以上問題。
  • 在這種應用場景下,使用SQLite 或者 Core Data比較好。使用這些技術你用特定的查詢語句就能只載入你需要的物件。
  • 在效能層面來講,SQLite和Core Data是很相似的。他們的不同在於具體使用方法。
  • Core Data代表一個物件的graph model,但SQLite就是一個DBMS。
  • Apple在一般情況下建議使用Core Data,但是如果你有理由不使用它,那麼就去使用更加底層的SQLite吧。
  • 如果你使用SQLite,你可以用FMDB這個庫來簡化SQLite的操作,這樣你就不用花很多經歷瞭解SQLite的C API了。

    三、高階

1、加速啟動時間。快速開啟app是很重要的,特別是使用者第一次開啟它時,對app來講,第一印象太太太重要了。你能做的就是使它儘可能做更多的非同步任務,比如載入遠端或者資料庫資料,解析資料。避免過於龐大的XIB,因為他們是在主執行緒上載入的。所以儘量使用沒有這個問題的Storyboards吧!一定要把裝置從Xcode斷開來測試啟動速度

2、使用Autorelease Pool。NSAutoreleasePool`負責釋放block中的autoreleased objects。一般情況下它會自動被UIKit呼叫。但是有些狀況下你也需要手動去建立它。假如你建立很多臨時物件,你會發現記憶體一直在減少直到這些物件被release的時候。這是因為只有當UIKit用光了autorelease pool的時候memory才會被釋放。訊息是你可以在你自己的@autoreleasepool裡建立臨時的物件來避免這個行為。

3、選擇是否快取圖片。常見的從bundle中載入圖片的方式有兩種,一個是用imageNamed,二是用imageWithContentsOfFile,第一種比較常見一點。

4、避免日期格式轉換。如果你要用NSDateFormatter來處理很多日期格式,應該小心以待。就像先前提到的,任何時候重用NSDateFormatters都是一個好的實踐。如果你可以控制你所處理的日期格式,儘量選擇Unix時間戳。你可以方便地從時間戳轉換到NSDate:

1
2
3
- (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp {
return [NSDate dateWithTimeIntervalSince1970:timestamp];
}

這樣會比用C來解析日期字串還快!需要注意的是,許多web API會以微秒的形式返回時間戳,因為這種格式在javascript中更方便使用。記住用dateFromUnixTimestamp之前除以1000就好了。

平時你是如何對程式碼進行效能最佳化的?

  • 利用效能分析工具檢測,包括靜態 Analyze 工具,以及執行時 Profile 工具,透過Xcode工具欄中Product->Profile可以啟動,
  • 比如測試程式啟動執行時間,當點選Time Profiler應用程式開始執行後.就能獲取到整個應用程式執行消耗時間分佈和百分比.為了保證資料分析在統一使用場景真實需要注意一定要使用真機,因為此時模擬器是執行在Mac上,而Mac上的CPU往往比iOS裝置要快。
  • 為了防止一個應用佔用過多的系統資源,開發iOS的蘋果工程師門設計了一個“看門狗”的機制。在不同的場景下,“看門狗”會監測應用的效能。如果超出了該場景所規定的執行時間,“看門狗”就會強制終結這個應用的程式。開發者們在crashlog裡面,會看到諸如0x8badf00d這樣的錯誤程式碼。

最佳化Table View

  • 正確使用reuseIdentifier來重用cells
  • 儘量使所有的view opaque,包括cell自身
  • 如果cell內現實的內容來自web,使用非同步載入,快取請求結果
  • 減少subviews的數量
  • 儘量不適用cellForRowAtIndexPath:,如果你需要用到它,只用一次然後快取結果
  • 使用rowHeight, sectionFooterHeight和sectionHeaderHeight來設定固定的高,不要請求delegate

UIImage載入圖片效能問題

  • imagedNamed初始化

  • imageWithContentsOfFile初始化

  • imageNamed預設載入圖片成功後會記憶體中快取圖片,這個方法用一個指定的名字在系統快取中查詢並返回一個圖片物件.如果快取中沒有找到相應的圖片物件,則從指定地方載入圖片然後快取物件,並返回這個圖片物件.

  • imageWithContentsOfFile則僅只載入圖片,不快取.

  • 載入一張大圖並且使用一次,用imageWithContentsOfFile是最好,這樣CPU不需要做快取節約時間.

  • 使用場景需要程式設計時,應該根據實際應用場景加以區分,UIimage雖小,但使用元素較多問題會有所凸顯.

    • 不要在viewWillAppear 中做費時的操作:viewWillAppear: 在view顯示之前被呼叫,出於效率考慮,方法中不要處理複雜費時操作;在該方法設定 view 的顯示屬性之類的簡單事情,比如背景色,字型等。否則,會明顯感覺到 view 有卡頓或者延遲。

    • 在正確的地方使用reuseIdentifier:table view用 tableView:cellForRowAtIndexPath:為rows分配cells的時候,它的資料應該重用自UITableViewCell。

    • 儘量把views設定為透明:如果你有透明的Views你應該設定它們的 -opaque屬性為YES。系統用一個最優的方式渲染這些views。這個簡單的屬性在IB或者程式碼裡都可以設定。

    • 避免過於龐大的XIB:儘量簡單的為每個Controller配置一個單獨的XIB,儘可能把一個View Controller的view層次結構分散到單獨的XIB中去, 當你載入一個引用了圖片或者聲音資源的nib時,nib載入程式碼會把圖片和聲音檔案寫進記憶體。

    • 不要阻塞主執行緒:永遠不要使主執行緒承擔過多。因為UIKit在主執行緒上做所有工作,渲染,管理觸控反應,回應輸入等都需要在它上面完成,大部分阻礙主程式的情形是你的app在做一些牽涉到讀寫外部資源的I/O操作,比如儲存或者網路。
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      // 選擇一個子執行緒來執行耗時操作
      dispatch_async(dispatch_get_main_queue(), ^{
      // 返回主執行緒更新UI
      });
      });

    • 在Image Views中調整圖片大小
      如果要在UIImageView中顯示一個來自bundle的圖片,你應保證圖片的大小和UIImageView的大小相同。在執行中縮放圖片是很耗費資源的.

講講你用Instrument最佳化動畫效能的經歷吧(別問我什麼是Instrument)

Apple的instrument為開發者提供了各種template去最佳化app效能和定位問題。很多公司都在趕feature,並沒有充足的時間來做最佳化,導致不少開發者對instrument不怎麼熟悉。但這裡面其實涵蓋了非常完整的計算機基礎理論知識體系,memory,disk,network,thread,cpu,gpu等等,順藤摸瓜去學習,是一筆巨大的知識財富。動畫效能只是其中一個template,重點還是理解上面問題當中CPU GPU如何配合工作的知識。

facebook啟動時間最佳化
1.瘦身請求依賴
2.UDP啟動請求先行快取
3.佇列序列化處理啟動響應

推薦文章

iOS 記憶體管理總結

iOS多執行緒面試題分析

面試題 擴充:常用框架和第三方框架

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69990324/viewspace-2741011/,如需轉載,請註明出處,否則將追究法律責任。

相關文章