iOS性能優化以內存管理:Analyze、Leaks、Allocations的使用和案例代碼

 

最近接了個小任務,和公司的iOS小夥伴們分享下instruments的具體使用,因而有了這篇博客...性能優化是一個很大的話題,這裏討論的主要是內存泄露部分。ios

 

一. 一些相關概念性能優化

不少人應該比較瞭解這塊內容了...能夠權當複習複習...ide

1.內存空間的劃分: 咱們知道,一個進程佔用的內存空間,包含5種不一樣的數據區:(1)BSS段:一般是存放未初始化的全局變量;(2)數據段:一般是存放已初始化的全局變量。(3)代碼段:一般是存放程序執行代碼。(4)堆:一般是用於存放進程運行中被動態分配的內存段OC對象(全部繼承自NSObject的對象)就存放在堆裏。(5)棧:由編譯器自動分配釋放,存放函數的參數值,局部變量等值。函數

棧內存是系統來管理的,所以咱們常說的內存管理,指的是堆內存的管理,也就是全部OC對象的建立和銷燬的管理。伴隨着iOS5的到來,蘋果推出了ARC(自動引用計數)技術,此模式下編譯器會自動在合適的地方插入retain、release、autorelease語句,也就是說編譯器會自動生成內存管理的代碼,解放了廣大程序猿的雙手,也基本上避免了內存泄露問題,可是呢...工具

2.內存泄露:百度百科給的定義是:"內存泄漏也稱做'存儲滲漏',用動態存儲分配函數動態開闢的空間,在使用完畢後未釋放,結果致使一直佔據該內存單元。直到程序結束。(其實說白了就是該內存空間使用完畢以後未回收)即所謂內存泄漏"。在iOS應用中的內存泄露,緣由通常有循環引用、錯用Strong/copy等。佈局

 

二. Analyze—靜態分析性能

顧名思義,靜態分析不須要運行程序,就能檢查到存在內存泄露的地方。測試

1. 使用方法:打開Xcode,command + shift + B;或者Xcode - Product - Analyze;優化

2. 常見的三種泄露情形:spa

(1)建立了一個對象,可是並無使用。Xcode提示信息:Value Stored to 'number' is never read。翻譯一下:存儲在'number'裏的值從未被讀取過。

(2)建立了一個(指針可變的)對象,且初始化了,可是初始化的值一直沒讀取過。Xcode提示信息:Value Stored to 'str' during its initialization is never read

(3)調用了讓某個對象引用計數加1的函數,但沒有調用相應讓其引用計數減1的函數。Xcode提示信息:Potential leak of an object stored into 'subImageRef'。 翻譯一下:subImageRef對象的內存單元有潛在的泄露風險。

3. 貼上Demo代碼:

/**
 * 情 形 一:建立了一個對象,可是並無使用。
 * 提示信息:Value Stored to 'number' is never read
 * 翻譯一下:存儲在'number'裏的值從未被讀取過,
 */
- (void)leakOne {
    NSString *str1 = [NSString string];
    NSNumber *number;
    number = @(str1.length);
    /*
     說咱們沒有讀取過它,那就讀取一下,好比打開下面這句代碼,對它發送class消息,就再也不會有這個提示了。
     固然最好的方法仍是將有關number的代碼都刪掉,由於,你只對number賦值,又不使用,那幹嗎建立出來呢。
     這是一個比較常見和典型的錯誤,也很容易檢查出來
     */
    // [number class];
}

/**
 * 情 形 二:建立了一個(指針可變的)對象,且初始化了,可是初始化的值一直沒讀取過。
 * 提示信息:Value Stored to 'str' during its initialization is never read
 */
- (void)leakTwo {
    NSString *str = [NSString string]; // 建立並初始化str,此時已經有一個內存單元保存str初始化的值
    // NSString *str; // 這樣就內存不泄露,由於str是可變的,只須要先聲明就行。
    // printf("str前 = %p\n",str);
    str = @"ceshi";             // str被改變了,指向了"ceshi"所在的地址,指針改變了,但以前保存初始化值的內存空間還未釋放,保存str初始化值的內存單元泄露了。
    // printf("str後 = %p\n",str); // 指針改變了
    [str class];
    
    // 再舉兩個例子,同理
    
    NSArray *arr = [NSArray array];
    // printf("arr前 = %p\n",arr);
    // NSArray *arr;            // 這樣就內存不泄露
    arr = @[@"1",@"2"];
    // printf("arr後 = %p\n",arr); // 指針改變了
    [arr class];
    
    CGRect rect = self.view.frame;
    // CGRect rect = CGRectZero; // 這樣就內存不泄露
    rect = CGRectMake(0, 0, 0, 0);
    NSLog(@"rect = %@",NSStringFromCGRect(rect));
}

/**
 * 情 形 三:調用了讓某個對象引用計數加1的函數,但沒有調用相應讓其引用計數減1的函數。
 * 提示信息:Potential leak of an object stored into 'subImageRef'
 * 翻譯一下:subImageRef對象的內存單元有潛在的泄露風險
 */
- (void)leakThree {
    CGRect rect = CGRectMake(0, 0, 50, 50);
    UIImage *image;
    CGImageRef subImageRef = CGImageCreateWithImageInRect(image.CGImage, rect); // subImageRef 引用計數 + 1;
    
    UIImage* smallImage = [UIImage imageWithCGImage:subImageRef];
    
    // 應該調用對應的函數,讓subImageRef的引用計數減1,就不會泄露了
    // CGImageRelease(subImageRef);
    
    [smallImage class];
    UIGraphicsEndImageContext();
}
View Code

貼上照片:

 

三. Leaks—內存泄露

Leaks是動態的內存泄露檢查工具,須要一邊運行程序,一邊檢測。

1.使用方法: 進入Xcode,command + control + i;或者Xcode - Xcode - Open Developer Tool - Instruments; 或者Xcode - Product - Profile。選擇Leaks。

2.界面詳情以下,這是運行時的界面

 測試了好幾個項目,發現用靜態分析檢查過的代碼,內存泄露都比較少。有2個項目能點的按鈕都點了,能進的頁面都進的,Leaks也沒檢測到泄露。

 

四. Allocations—內存分配

Allocations是檢測程序運行過程當中的內存分配狀況的,也須要同時運行着程序。1.打開方法:同上。2.界面狀況以下:

截圖二:

 雙擊某一個方法,一樣會跳轉到代碼裏,會有每一句代碼對應的內存分配狀況,根據這些信息,能夠對程序裏不一樣代碼的內存佔用狀況有一些認識,並進行鍼對性的優化。

 

五. 平時寫代碼的一些tip

說到底呢,instruments只是一組工具,幫助咱們分析代碼的工具,可能檢查的出的內存問題和性能問題,確定仍是由代碼形成的。養成良好的代碼習慣,纔是根本的解決方法。首先是避免出現靜態分析裏提到的三種常見內存泄露問題,我測試的好幾個項目裏,都有出現這個問題。

tip:哪些狀況會增長CPU的消耗?「搬運自YYKit的做者的博客(強烈推薦):ibireme

(1) 建立對象、調整對象屬性、銷燬對象。(2)佈局計算和Autolayout。(3)文本的計算和渲染。(4)圖片的解碼和繪製。「用Time Profiler分析一下,能夠更直觀地感覺到哪些操做比較耗時,使用方法同上。」

小結:作好cell等可複用對象的重用;能夠只建立一次的對象,不要建立屢次(好比頁面的某個功能彈窗);用較少的對象和方法調用去實現功能;將耗時的操做放在子線程等能夠對內存和性能作一些優化

相關文章
相關標籤/搜索