MJiOS底層筆記--性能優化

本文屬筆記性質,主要針對本身理解不太透徹的地方進行記錄。

推薦系統直接學習小碼哥iOS底層原理班---MJ老師的課確實不錯,強推一波。ios


CPU和GPU

CPU(Central Processing Unit,中央處理器)

  1. 對象的建立和銷燬
  2. 對象屬性的調整
  3. 佈局計算
  4. 文本的計算和排版
  5. 圖片的格式轉換和解碼
  6. 圖像的繪製(Core Graphics)

GPU(Graphics Processing Unit,圖形處理器)

  1. 紋理的渲染 用來就是用來給屏幕展現的數據格式

協做工做

  1. CPU負責計算位置、大小、顏色等等一系列參數
  2. GPU負責將數據進行渲染後放入緩衝區備用
  3. 控制器從緩衝區讀取數據並展現到屏幕上
  4. 在iOS中是雙緩衝機制,有前幀緩存、後幀緩存。能夠提升渲染效率

屏幕成像原理

顯示器將要展現一頁數據git

  1. 發送VSync
  2. 從上至下依次發出HSync逐行進行填充
  3. 循環步驟1

卡頓產生的緣由

VSync信號

  1. 會將緩衝區的數據顯示到屏幕上
  2. 立刻讓CPU和GPU開始下一幀的處理

60FPS

按照60FPS的刷幀率,每隔16ms就會有一次VSync信號github


卡頓優化 - CPU

  1. 儘可能用輕量級的對象數據庫

    好比用不到事件處理的地方,能夠考慮使用CALayer取代UIView緩存

  2. 不要頻繁地調用UIView的相關屬性bash

    好比frame、bounds、transform等屬性,儘可能減小沒必要要的修改網絡

  3. 儘可能提早計算好佈局併發

    在有須要時一次性調整對應的屬性,不要屢次修改屬性app

  4. Autolayout會比直接設置frame消耗更多的CPU資源異步

  5. 圖片的size最好恰好跟UIImageView的size保持一致

    減小ImageView對圖片的伸縮操做

  6. 控制一下線程的最大併發數量

  7. 儘可能把耗時的操做放到子線程

    充分利用多核優點

    文本處理(尺寸計算、繪製)

    圖片處理(解碼、繪製)

    [UIImage imageNamed:@"timg"]加載出來的圖片是未解碼的,當UIImageView須要被展現的時候纔會由CPU進行解碼操做。而這個解碼操做默認在主線程進行。

    咱們能夠將解碼操做轉移到異步。

- (void)image
{
    UIImageView *imageView = [[UIImageView alloc] init];
    imageView.frame = CGRectMake(100, 100, 100, 56);
    [self.view addSubview:imageView];
    self.imageView = imageView;

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // 獲取CGImage
        CGImageRef cgImage = [UIImage imageNamed:@"timg"].CGImage;

        // 獲取圖片信息
        CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(cgImage) & kCGBitmapAlphaInfoMask;
        BOOL hasAlpha = NO;
        if (alphaInfo == kCGImageAlphaPremultipliedLast ||
            alphaInfo == kCGImageAlphaPremultipliedFirst ||
            alphaInfo == kCGImageAlphaLast ||
            alphaInfo == kCGImageAlphaFirst) {
            hasAlpha = YES;
        }

        // bitmapInfo
        CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;
        bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;

        // 獲取圖片大小size
        size_t width = CGImageGetWidth(cgImage);
        size_t height = CGImageGetHeight(cgImage);

        // 建立圖形上下文
        CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, CGColorSpaceCreateDeviceRGB(), bitmapInfo);

        // 將圖片繪製到上下文中
        CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage);

        // 獲取解碼後的獲取CGImage
        cgImage = CGBitmapContextCreateImage(context);

        // 將解碼後的CGImage包裝成UIImage
        UIImage *newImage = [UIImage imageWithCGImage:cgImage];

        // 釋放資源
        CGContextRelease(context);
        CGImageRelease(cgImage);

        // back to the main thread
        dispatch_async(dispatch_get_main_queue(), ^{
            //回到主線程設置圖片
            self.imageView.image = newImage;
        });
    });
}
複製代碼

卡頓優化 - GPU

  1. 儘可能減小視圖數量和層次 每個View都須要被計算並渲染
  2. 儘可能避免短期內大量圖片的顯示 每張圖片都須要被單獨渲染,儘量將多張圖片合成一張進行顯示
  3. GPU能處理的最大紋理尺寸是4096x4096 一旦超過這個尺寸,就會佔用CPU資源進行處理,因此紋理儘可能不要超過這個尺寸
  4. 減小透明的視圖(alpha<1) 不透明的就設置opaque爲YES
  5. 儘可能避免出現離屏渲染

離屏渲染

在OpenGL中,GPU有2種渲染方式

  1. On-Screen Rendering:

    當前屏幕渲染,在當前用於顯示的屏幕緩衝區進行渲染操做

  2. Off-Screen Rendering:

    離屏渲染,在當前屏幕緩衝區之外新開闢一個緩衝區進行渲染操做

爲何會使用離屏渲染

當使用某些效果時,圖層的效果處理起來很費時,有可能超過16.67ms致使丟幀。系統會在當前屏幕的緩衝區以外另開闢一個緩衝區去預合成。

在VSync(垂直脈衝)信號做用下,視頻控制器每隔16.67ms就會去幀緩衝區(當前屏幕緩衝區)讀取渲染後的數據;可是有些效果被認爲不能直接呈現於屏幕前,而須要在別的地方作額外的處理,進行預合成。

當使用圓角,陰影,遮罩的時候,圖層屬性的混合體被指定爲在未預合成以前(下一個VSync信號開始前)不能直接在屏幕中繪製,因此就須要屏幕外渲染。

你能夠這麼理解. 老闆叫我短期間內作一個app.我一我的能作,可是時間過短,因此我得讓我朋友一塊兒來幫着我作.(性能消耗: 也就是耗 你跟你朋友之間溝通的這些成本,多浪費啊).可是沒辦法 誰讓你作不完呢.

離屏渲染消耗性能的緣由

  1. 須要建立新的緩衝區

  2. 頻繁的切換緩衝區

    離屏渲染的整個過程,須要屢次切換上下文環境,先是從當前屏幕(On-Screen)切換到離屏(Off-Screen);等到離屏渲染結束之後,將離屏緩衝區的渲染結果顯示到屏幕上,又須要將上下文環境從離屏切換到當前屏幕

哪些操做會觸發離屏渲染?

  1. 光柵化

    layer.shouldRasterize = YES

  2. 遮罩

    layer.mask

  3. 圓角

    同時設置layer.masksToBounds = YES、layer.cornerRadius大於0 考慮經過CoreGraphics繪製裁剪圓角,或者叫美工提供圓角圖片

  4. 陰影,layer.shadowXXX

    若是設置了layer.shadowPath就不會產生離屏渲染

  5. 富文本效果


卡頓檢測

平時所說的「卡頓」主要是由於在主線程執行了比較耗時的操做

能夠添加Observer到主線程RunLoop中,經過監聽RunLoop狀態切換的耗時,以達到監控卡頓的目的。

一般是監聽Runloop被喚醒到休眠以前這段時間的時長,連續超過閥值必定次數就打印當前主線程的堆棧。

MJ裏的項目是LXDAppFluecyMonitor


耗電

主要來源

  1. CPU處理,Processing
  2. 網絡,Networking
  3. 定位,Location
  4. 圖像,Graphics

耗電優化

  1. 儘量下降CPU、GPU功耗

  2. 少用定時器

  3. 優化I/O操做

儘可能不要頻繁寫入小數據,最好批量一次性寫入

讀寫大量重要數據時,考慮用dispatch_io,其提供了基於GCD的異步操做文件I/O的API。用dispatch_io系統會優化磁盤訪問

數據量比較大的,建議使用數據庫(好比SQLite、CoreData)

  1. 網絡優化

減小、壓縮網絡數據

若是屢次請求的結果是相同的,儘可能使用緩存

使用斷點續傳,不然網絡不穩定時可能屢次傳輸相同的內容

網絡不可用時,不要嘗試執行網絡請求

讓用戶能夠取消長時間運行或者速度很慢的網絡操做,設置合適的超時時間

批量傳輸,好比,下載視頻流時,不要傳輸很小的數據包,直接下載整個文件或者一大塊一大塊地下載。若是下載廣告,一次性多下載一些,而後再慢慢展現。若是下載電子郵件,一次下載多封,不要一封一封地下載

  1. 定位優化

若是隻是須要快速肯定用戶位置,最好用CLLocationManager的requestLocation方法。定位完成後,會自動讓定位硬件斷電

若是不是導航應用,儘可能不要實時更新位置,定位完畢就關掉定位服務

儘可能下降定位精度,好比儘可能不要使用精度最高的kCLLocationAccuracyBest

須要後臺定位時,儘可能設置pausesLocationUpdatesAutomatically爲YES,若是用戶不太可能移動的時候系統會自動暫停位置更新

儘可能不要使用startMonitoringSignificantLocationChanges,優先考慮startMonitoringForRegion:

  1. 硬件檢測優化

用戶移動、搖晃、傾斜設備時,會產生動做(motion)事件,這些事件由加速度計、陀螺儀、磁力計等硬件檢測。在不須要檢測的場合,應該及時關閉這些硬件


APP的啓動

冷啓動 && 熱啓動

APP的啓動能夠分爲2種

  1. 冷啓動(Cold Launch):從零開始啓動APP

  2. 熱啓動(Warm Launch):APP已經在內存中,在後臺存活着,再次點擊圖標啓動APP

啓動時間的優化

APP啓動時間的優化,主要是針對冷啓動進行優化

冷啓動時間分析

經過添加環境變量能夠打印出APP的啓動時間分析(Edit scheme -> Run -> Arguments) DYLD_PRINT_STATISTICS設置爲1 若是須要更詳細的信息,那就將DYLD_PRINT_STATISTICS_DETAILS設置爲1

total time: 1.4 seconds (100.0%)
  total images loaded:  257 (0 from dyld shared cache)
  total segments mapped: 764, into 103339 pages with 7230 pages pre-fetched
  total images loading time: 720.70 milliseconds (48.1%)
  total load time in ObjC:  71.93 milliseconds (4.8%)
  total debugger pause time: 539.08 milliseconds (36.0%)
  total dtrace DOF registration time:   0.12 milliseconds (0.0%)
  total rebase fixups:  2,519,273
  total rebase fixups time: 635.12 milliseconds (42.4%)
  total binding fixups: 283,078
  total binding fixups time:  36.50 milliseconds (2.4%)
  total weak binding fixups time:   0.52 milliseconds (0.0%)
  total redo shared cached bindings time:  52.57 milliseconds (3.5%)
  total bindings lazily fixed up: 0 of 0
  total time in initializers and ObjC +load:  31.39 milliseconds (2.0%)
                         libSystem.B.dylib :   2.92 milliseconds (0.1%)
               libBacktraceRecording.dylib :   3.50 milliseconds (0.2%)
                            CoreFoundation :   1.74 milliseconds (0.1%)
                                Foundation :   2.02 milliseconds (0.1%)
                libMainThreadChecker.dylib :  18.89 milliseconds (1.2%)
total symbol trie searches:    132606
total symbol table binary searches:    0
total images defining weak symbols:  20
total images using weak symbols:  61
複製代碼

冷啓動

APP的啓動由dyld主導,將可執行文件加載到內存,順便加載全部依賴的動態庫

並由runtime負責加載成objc定義的結構

全部初始化工做結束後,dyld就會調用main函數

APP的冷啓動能夠歸納爲3大階段

  1. dyld

    Apple的動態連接器,能夠用來裝載Mach-O文件(可執行文件、動態庫等)

    裝載APP的可執行文件,同時會遞歸加載全部依賴的動態庫

    當dyld把可執行文件、動態庫都裝載完畢後,會通知Runtime進行下一步的處理

  2. runtime

    調用map_images進行可執行文件內容的解析和處理

    load_images中調用call_load_methods,調用全部ClassCategory+load方法

    進行各類objc結構的初始化(註冊Objc類、初始化類對象等等)

    調用C++靜態初始化器和__attribute__((constructor))修飾的函數

    到此爲止,可執行文件和動態庫中全部的符號(Class,Protocol,Selector,IMP,…)都已經按格式成功加載到內存中,被runtime 所管理

  3. main


安裝包瘦身

安裝包(IPA)主要由可執行文件、資源組成

資源(圖片、音頻、視頻等)

  1. 採起無損壓縮

  2. 去除沒有用到的資源:

    github.com/tinymind/LS…

可執行文件瘦身

  1. 編譯器優化 Strip Linked Product、Make Strings Read-Only、Symbols Hidden by Default設置爲YES

去掉異常支持,Enable C++ Exceptions、Enable Objective-C Exceptions設置爲NO, Other C Flags添加-fno-exceptions

  1. 利用AppCode(www.jetbrains.com/objc/) 檢測未使用的代碼:

    菜單欄 -> Code -> Inspect Code

  2. 編寫LLVM插件檢測出重複代碼、未被調用的代碼

相關文章
相關標籤/搜索