最近接了個小任務,和公司的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(); }
貼上照片:
三. 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等可複用對象的重用;能夠只建立一次的對象,不要建立屢次(好比頁面的某個功能彈窗);用較少的對象和方法調用去實現功能;將耗時的操做放在子線程等能夠對內存和性能作一些優化。