iOS 項目優化


前言

iOS性能優化系列篇之“優化總體原則”

  • 不要提前過度優化
  • 要找到性能瓶頸
  • 要在不同性能指標間權衡
  • 要理解優化任務的底層運行機制
  • 要有技術保障體系

一、啟動速度優化

1.1 學習文章

1.2 操作步驟

  1. 查看啟動時間

    配置 Xcode 環境變量在日志中打印啟動時間:

    • 打開工程 -> Edit Scheme -> Run -> Environment Variables

    根據需要添加 DYLD_PRINT_STATISTICSDYLD_PRINT_STATISTICS_DETAILS 環境變量。value 設置為 1(表示 YES),開啟這個功能。


    Total pre-main time: 617.58 milliseconds (100.0%)
             dylib loading time: 472.75 milliseconds (76.5%)
    rebase/binding time: 27.01 milliseconds (4.3%)
    ObjC setup time: 28.90 milliseconds (4.6%)
    initializer time: 88.76 milliseconds (14.3%)
    slowest intializers :
    libSystem.B.dylib : 8.81 milliseconds (1.4%)
    libMainThreadChecker.dylib : 14.42 milliseconds (2.3%)
    AFNetworking : 18.43 milliseconds (2.9%)
    Realm : 20.98 milliseconds (3.3%)
    CYKJBasic : 12.96 milliseconds (2.0%)

    包括執行以下步驟的所有時間:

    1. 解析鏡像
    2. 映射鏡像
    3. Rebase 鏡像
    4. Bind 鏡像
    5. 鏡像初始化
    6. 調用 main()方法
    7. 調用 UIApplicationMain() 方法
    8. 調用 applicationWillFinishLaunching 回調
  2. 優化 main() 函數之后的執行時間

    • 使用代碼繪制 UI,減少或者不用 xib 和 storyboard
    • 延遲初始化和加載不必要的 UIViewController 和 View。
    • 使用后台線程處理耗時的任務
    • 能延遲初始化的盡量延遲初始化
  3. 優化 main() 函數之前的執行時間

    Session 所要傳達的內容:

    • 使用 DYLD_PRINT_STATISTICS 測試啟動加載時間
    • 減少自定義的動態庫集成
    • 精簡原有的 Objective-C 類和代碼
    • 移除靜態的初始化操作
    • 使用更多的 Swift 代碼

    優化:

    • loading dylib

      減少動態庫的數量。盡量保證將 App 現有的非系統級的動態庫個數保證在 6 個以內。采取的方式:1、合並動態庫;2、使用靜態庫。

    • rebase/binding

      Rebase 和 Binding 都是為了解決指針引用的問題。對於 Objective-C 開發來說,主要的時間消耗在 Class/Method 的符號加載上,所以常見的優化方案是:

      • 減少 __DATA 段中的指針數量。
      • 合並 Category 和功能類似的類,減少唯一 Selector 的個數。
      • 刪除無用的方法和類。
      • 多用 Swift Structs,因為 Swfit Structs 是靜態分發的。
      • 減少 c++ 虛函數
    • ObjC setup time

      盡量減少類的數量,可以達到減少這一部分的時間。

    • Initializers

      • 用 initialize 替代 load。(注意:要根據實際情況替換)
      • 減少使用 c/c++ 的 __atribute__((constructor))。推薦使用 dispatch_once()、pthread_once()、std:once()等方法;
      • 推薦使用 swift
      • 不要在初始化中調用 dlopen() 方法,因為加載過程是單線程,無鎖,如果調用 dlopen 則會變成多線程,會開啟鎖的消耗,同時有可能死鎖
      • 不要在初始化中創建線程

1.3 實際處理

實際處理時因為要考慮團隊的原因,所以只采用了一下三個步驟:

  1. 刪除不需要的三方庫,因為工程中用 use_frameworks! pod 進來的庫是動態庫。
  2. 將大部分 +load 方法改為 +initialize 方法,保留部分必要的 +load 方法;
  3. 動態庫 -> 靜態庫,技術上實現了,討論后最終棄用。
  4. 刪除無用的方法和類,減少 rebase/binding 時間。。

1.4 處理效果

慈雲 app 啟動時間有 700 ~ 850ms,降為 600 ~ 700ms。

如果采用靜態方式 pod,可以降為 450 ~ 550ms。

二、接口請求優化

主要集中處理一級 tab 的切換體驗,頻繁的調用接口,頻繁的刷新界面顯然是影響用戶體驗的。優化的思路有以下幾點:

  1. 使用 loading 框 + 默認灰色矩形視圖;


  2. 使用本地緩存

  3. 每隔 15s(或者 10s) 以上才請求一次,防止頻繁觸發請求

    @property (nonatomic, assign) CFTimeInterval lastTi;
    - (void)viewWillAppear:(BOOL)animated
    {
    [super viewWillAppear:animated];
    CFTimeInterval nowTi = CACurrentMediaTime();
    // 10 秒內不請求
    if (nowTi - self.lastTi > 10) {
    self.reqData.pageNo = 1;
    [self.service requestPatientConsultList:self.reqData];
    self.lastTi = nowTi;
    }
    }
    - (void)makeRequestAction
    {
    // 接口請求
    }

    CACurrentMediaTime() 在退到后台、手動修改設備時間后沒有影響。

  4. 對數據進行判斷,數據沒有更新不需要刷新界面。

    @property (nonatomic, copy) NSString * primaryCompareMD5;
    // 初始值 @""
    self.primaryCompareMD5 = @"";
    SELF_WEEK;
    // 異步執行
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    @synchronized (self) {
    // 這里的 data 是要判斷的接口數據
    NSString * string = [data componentsJoinedByString:@""];
    if (!string) {
    string = @"";
    }
    NSString * md5 = [CYDXUtil doMd5:string];
    SELF_STRONG;
    // 數據未發生改變,直接返回
    if ([strongSelf.primaryCompareMD5 isEqualToString:md5]) {
    return;
    }
    strongSelf.primaryCompareMD5 = md5;
    // 主線程刷新界面
    dispatch_sync(dispatch_get_main_queue(), ^{
    [strongSelf.tableView reloadData];
    });
    }
    });

這里的策略還是存在點問題:

  1. 不能處理好未讀標識。看實際情況,處理從子界面返回時的邏輯。

三、界面滑動優化

3.1 監測界面卡頓工具

  1. 使用系統的 Instrument - CoreAnimation
  2. 使用系統的 Instrument - Time Profiler
  3. YYFPSLabel

在未滑動時,Instrument - CoreAnimation 顯示的是 0 FPS。

YYFPSLabel 顯示的是 60FPS。

卡頓問題修改后,Instrument - CoreAnimation 需要在設備上運行一遍,然后重新用工具檢測;YYFPSLabel 可以直接加入工程,可視化,更加的友好,方便。

Time Profiler 可以查看哪些方法占用時間多,優化那些可以優化的。

3.2 優化處理

頂部菜單欄

Instrument之Core Animation工具

  1. 圖層混合

    Xcode 頂部菜單欄 -> Debug -> View Debugging -> Rendering -> Color Blended Layers

    正常的為綠色;出現圖層混合時為紅色。

    • 確保控件的 opaque 屬性設置為 true,確保 backgroundColor 和父視圖顏色一致且不透明;
    • 如無特殊需要,不要設置低於 1 的 alpha 值;
    • 確保 UIImage 沒有 alpha 通道;
  2. 圖片縮放

    Xcode 頂部菜單欄 -> Debug -> View Debugging -> Rendering -> Color Misaligned Images

    如果圖片需要縮放則標記為黃色,如果沒有像素對齊則標記為紫色。

    這個看情況是否優化,是否需要 UI 配合。

    • 確保圖片大小和frame一致,不要在滑動時縮放圖片;
    • 確保圖片顏色格式被 GPU 支持,避免勞煩 CPU 轉換;
  3. 離屏渲染

    Xcode 頂部菜單欄 -> Debug -> View Debugging -> Rendering -> Color Offscreen-Rendered Yellow

    觸發離屏渲染的地方標記為黃色。

    下面的情況或操作會引發離屏渲染:

    • 為圖層設置遮罩(layer.mask)
    • 同時設置 layer.masksToBounds 和 corneRadius 屬性設置為 true
    • 將圖層的 layer.allowsGroupOpacity 屬性設置為 YES 和 layer.opacity < 1.0
    • 為圖層設置陰影(layer.shadow*)
    • 為圖層設置 layer.shouldRasterize = true
    • 文本(任何種類,包括 UILabel、CATextLayer、CoreText 等)
    • 使用 CGContext 在 drawRect: 方法中繪制大部分情況下會導致離屏渲染,甚至僅僅是一個空的實現

    解決:

    • 使用 CoreGraphics 繪制圓角,不使用 mask 或 masksToBounds + corneRadius;
    • 設置陰影是使用 shadowPath;
    • UILabel 如果不是透明的,設置 opaque = YES,Clip To bounds = YES,backgroundColor;
    • 設置圖片圓角可以讓 UI 切圖,也可以使用 CoreGraphics 繪制圖片的方式。在 iOS 9.0 以上,圖片設置圓角不會觸發離屏渲染。

    iOS 陰影,圓角,避免離屏渲染
    iOS - 圓角的設計策略 - 及繪畫出沒有離屏渲染的圓角

  4. UITableViewCell 優化

    • Cell 復用;
    • 提前計算並緩存 Cell 的高度;
    • 減少 subviews 的個數和層級;
    • 少用 subviews 的透明圖層;
    • 如果不是透明視圖,背景色不要使用 clearColor;
    • 注意離屏渲染問題;
    • 圖片提前在子線程異步解碼,SDWebImage 已經實現;
    • 異步繪制(自定義 Cell 繪制)VVeboTableViewDemoYYAsyncLayerAsyncDisplayKit
    • 滑動時,按需加載。注意:這個會導致滑動時出現大量空白,不友好。
    • 盡量顯示“大小剛好合適的”圖片資源
    • 避免同步的從網絡、文件獲取數據

四、內存占用

主要檢查工程中的內存泄露、循環引用的問題。

  1. instrument - Allocations 動態分析
  2. Analyze 靜態分析
  3. 騰訊 MLeaksFinder
  4. 臉書 FBMemoryProfiler

說明:

  1. MLeaksFinder 效果比較好,能查找工程中出現循環引用的的場景,在慈雲找到十幾處循環引用。
  2. 官方的 instrument 工具效果一般,在慈雲 app 只檢測出了 3 處循環引用,2 處內存泄露;
  3. Analyze 靜態分析可檢測出“創建了但未被使用的”變量,需要結合代碼邏輯,小心處理。
  4. 臉書的 FBMemoryProfiler 工具不大好用,主要是記錄內存的開辟與銷毀。

如果要更多的處理內存占用問題,需要分析工程中的代碼邏輯,通過使用不同的存儲方式、容器類、加載數據方式等,達到減少內存使用的目的。

五、縮小 ipa 包大小

愛奇藝移動應用優化之路:如何讓崩潰率小於千分之二

安裝包大小的優化,主要包含兩大塊:資源大小的優化和二進制大小的優化。

5.1 資源大小的優化

資源大小的優化主要包括以下幾個方面:

  1. 資源壓縮
  2. 未使用、重復資源的刪除
  3. 資源上雲

具體步驟:

  1. 資源壓縮

    Xcode 的編譯選項中,提供了 Compress PNG FilesRemove Text MetaData From PNG Files

    但是由於 PNG 是無損壓縮,經過 Xcode 壓縮后的圖片資源,依然很大。

    使用 pngquant 對大多數的 32 位圖進行了處理,將其轉為 8 位圖,並且使用 Zopfli 進行了壓縮,這樣整體的 PNG 圖片資源大概被壓縮了 70% 左右。這里要注意,由於一些漸變背景的顏色覆蓋范圍較大,轉為 8 位圖顏色丟失較大,表現效果會差很多,所以這些圖片要謹慎處理。

  2. 未使用、重復資源的刪除

  3. 資源上雲

    資源上雲可以有效減少包內資源,唯一要注意的是這些資源由於是 lazy load,所以比較適合層級較深的頁面使用。

5.2 其他處理

  1. 配置編譯選項 Generate Debug Symbols 設置為 NO;

  2. 舍棄架構,如:armv7,根據實際選擇。

  3. 編譯的版本必須是 release 版本,

  4. 查找內部使用到的第三方庫,一方面可以進行刪減代碼,用不到的類,直接刪除,還有第三方庫中的圖片資源統統刪除掉,如果能夠自己手寫實現的,那費功夫自己寫吧

六、引用庫升級及替換

持續更新的第三方庫也會對性能做出優化,在替換前,需先確定不會給項目引入問題。

七、更多優化

iOS 性能調試
iOS視圖成像理論及性能優化
iOS性能優化系列篇之“列表流暢度優化”


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM