iOS 對付內存泄漏,來講說個人調試方法

      蘋果在iOS5推出了ARC(自動引用計數)技術,此模式下編譯器會自動在合適的地方插入retain、release、autorelease語句,也就是說編譯器會自動生成內存管理的代碼,可是不免會有內存泄漏。因此須要調試技巧來處理各類閃退崩潰和內存泄漏等問題。(遇到最無解的是在調試過程當中忽然間彈出「Lost Connection to "iPad 2」...)。-- linweidahtml

內存空間的劃分

 一個進程佔用的內存空間,包含5種不一樣的數據區:ios

(1)BSS段:一般是存放未初始化的全局變量;git

(2)數據段:一般是存放已初始化的全局變量。程序員

(3)代碼段:一般是存放程序執行代碼。github

(4)堆:通常由程序員分配釋放,也就是全部OC對象的建立和銷燬的管理。。若程序員不釋放,程序結束時可能由OS收回。express

(5)棧:有操做系統自動分配釋放,存放函數的參數值、局部變量。 xcode

char c; // 棧內存分配
char * p = new char[3]; // 堆內存分配,將地址賦值給p

內存泄漏

內存泄漏也稱做「存儲滲漏」,用動態存儲分配函數動態開闢的空間,在使用完畢後未釋放,結果致使一直佔據該內存單元。直到程序結束。(其實說白了就是該內存空間使用完畢以後未回收)即所謂內存泄漏"。app

調試技巧

一、全局斷點或逐步斷點 + NSLog打印驗證框架

二、(llbd)調試,打斷點在打印區輸入「po xxx」http://www.jianshu.com/p/073979bccd2fide

三、Instruments調試

四、靜態分析Analyze

五、UITesting替代Instruments中的自動化測試Automation

普通操做

基本的斷點操做以下

 

點擊那個黑列列就建立了一個斷點,再次點擊就臨時取消這個斷點(可是不刪除),長按那個斷點拖出去就刪除了(mac os的系統工程師就是稀飯拖動的快感),固然也能夠右鍵那個建立的斷點,會彈出相應地菜單。
固然也還能夠監視某個變量!

在對象視圖中,右鍵某個對象,點擊「Watch ‘XXX’」就完成XXX對象的監視了。

這裏我監視了lab這個UILabel的變量,每當這個變量進行更新它的信息就會被打印到控制檯。
好 吧!咱們最基本的建立斷點的工做已經學會了,Xcode舒服在什麼地方呢?就是不分Debug模式和Run模式的,能夠說是無縫切換的,你只要沒有建立斷 點,那麼就是Run的正常模式,若是建立了斷點而且運行到斷點處,就自動進入Debug模式咯,不像某EC開頭的IDE,控制面板就像開飛機的同樣,幾萬 個按鈕覺得很強大,其實只用了Run和Stop,還有什麼Debug模式,App模式……,果真Xcode的優越感在對比中更增強烈了,舒服到極點呀,就 像夏天的海風拂過菊花,嗯是的 就是那種感受!
咱們建立好了斷點,運行到斷點就自動停下來了,像這樣:

這些Debug的最基本操做技能是每個入門的iOS開發者都要掌握的,應該當成一種本能,就像狗愛吃翔同樣(噢 對不起 博主不是歧視狗的意思,博主也養過狗,很二逼可是從不吃翔!真的據我所知它歷來不吃翔的,這裏只是比喻只是比喻)。

全局斷點(Global BreakPoint)

有時候在程序出錯的時候不能能準肯定位到奔潰的那一行代碼,而是直接跑到main循環或者Appdelegate裏面, 或者會給你這樣的提示:

EXEC_BAD_ACCESS:

是否是有種想哭的衝動?尼瑪~至少給我一些堆棧信息也好呀~……這個時候你千萬不要砸鼠標和鍵盤哦,一切都是主機在運行,你砸鼠標和鍵盤有什麼用呢?應該是踢主機呀~~,如今有了全局斷點,孃親不再擔憂你砸鼠標了,你只須要這樣:

在Debug導航面板進行上圖的操做,你就創建了全局斷點,這樣只要遇到錯誤,debug程序就會自動定位到棧底的信息,也就是你最早出錯的代碼的那一行,這樣你就能夠快樂的debug拉~~

條件斷點(Condational Breakpoints)

從 前有一個遊戲,叫作擼啊擼,有些玩家他們知道怎麼操做,會放技能會走路,可是他們不知道買裝備,玩了一局下來,鞋子小刀都沒有買。我爲何講這個故事呢? 由於不少小朋友學東西和玩遊戲同樣,看完前面的幾種調試技能,就覺得本身已經屌爆無敵了,其實他們不過是出門不帶裝備的玩家,若是隻是使用了以上的調試技 能只能說是低玩,在高大的逼優雞面前根本就是會被瞬秒的那種,因此學會裝備本身才是王道!條件斷點,就是學會有的放矢!

咱們來看一段代碼

你是否是想問博主爲什麼那麼風騷,居然上了Swift了!!我此刻只想吟一首溼:別人笑我太淫蕩,我家住在黃鶴樓。
反正這個年代你們都是吃飽了撐着的,博主也是,因此就學學Swift咯。
咱們若是在一個循環裏面使用了斷點,若是這個循環執行了100萬次,那你的斷點要執行那麼屢次,你不以爲蛋蛋都涼了的憂傷麼?因此咱們這麼作:

這樣只有遍歷到c==「H」的時候 斷點纔會被觸發。

是否是很棒呢!
有些童鞋的鈦合金狗眼已經看到了編輯斷點那裏有一個Action的東西,那是什麼呢?
這個是很是強大的,能夠在你斷點的位置,執行各類操做,好比執行腳本命令,控制檯命令(能夠制定調試信息自定義保存)、打印信息等,
博主最喜歡的就是這個Log message啦,簡單粗暴!根本就不須要print啊NSLog嘛,直接在斷點的Action打印就行了(其實這個是Xcode和調試器結合的高能產物,下面再介紹)。具體能夠這樣:

其 實剛剛博主撒謊了,博主最喜歡的Action並非Log Message,而是Sound,顧名思義嘛,斷點射在Bug上,這樣遇到斷點就會發出聲音,聽到我本身設置的聲音,我就知道是什麼Bug了,聽聲識 Bug,呵呵,EXEC_BAD_ACCESS的錯誤我設置成了波多野老師的聲音,unrecognized selector send to instancd的錯誤我設置成了蒼老師的…… 不要問我係統怎麼沒有吉澤明步的聲音,我根本就不知道誰是吉澤明步。

固然還有更增強大的條件斷點就是這貨啦

添加以後在 Symbol 一欄輸入 viewDidLoad。
這樣一來,在程序中全部的 viewDidLoad 方法被調用時都會觸發斷點。


固然,咱們也能夠僅僅爲特定的某個類的方法添加斷點。在 Symbol 一欄輸入 [ClassName viewDidLoad] (Objective-C) 或 ClassName.viewDidLoad (Swift) 便可。
好比:unrecognized selector sent to instance 0xaxxxx 這種錯誤,這個instance能夠這樣快速定位

NSLog

小 夥伴們第一節課學習ViewController的生命週期的時候,老師確定很猥瑣的教了你們,在viewController的每一個生命週期的方法中使 用了NSLog來偷窺!沒錯,這樣其實就是最簡單爆炸的跟蹤生命週期的方法了,不過系統本身的NSLog真心有點羸弱,輸出的信息太少,根本就不能知足我 們的慾望,這裏我教你們強化你的Log!!
能夠用下面的這段宏

//A better version of NSLog
#define NSLog(format, ...) do { \
fprintf(stderr, "<%s : %d> %s\n", \
[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], \
__LINE__, __func__); \
(NSLog)((format), ##__VA_ARGS__); \
fprintf(stderr, "-------\n"); \
} while (0)

這樣打印出來的東西纔像話嘛(其實NSLog的打印是很是低效的,甚至比print低100倍,感興趣本身翻翻蘋果手冊咯)。 
使用objc語言(強類型)而且用NSLog打印的時候,經常搞不清楚NSLog(@「%?」,xxx) xxx這種類型該是什麼什麼類型輸出,應該是%d呢仍是%@亦或是%f???傻傻分不清楚~,因此玩轉NSLog你應該要知道如下這幾個全局方法!

開啓殭屍對象(Enable NSZombie Objects)

Xcode能夠把那些已經release掉得對象,變成「殭屍」,當咱們訪問一個Zombie對象時,Xcode能夠告訴咱們正在訪問的對象是一個不該該存在的對象了。由於Xcode知道這個對象是什麼,因此可讓咱們知道這個對象在哪裏,以及這是何時發生的。
因此Zombies是你的好基友!他可讓你輸出的信息更具體!!
具體這樣作:


本身再試試輸出Object的信息咯,是否是很棒呢?
殭屍只能用在模擬器和OC語言哦~

Console(lldb 命令)

咱們的目標是要武裝到鼻毛!console窗口你們知道就是哪一個黑乎乎好多字會滾出來,尤爲是被逼優雞幹到的時候,那麼同窗們有沒有遇到這種console呢


首先!你得先crash或者把程序斷下來!直到你看到圖16的(lldb)字樣出現,你就能夠敲命令了~~
每次你想查看變量,常量,你要從新寫NSLog去打印,而後從新編譯,去執行,重頭開始?太累了,有了lldb你只要這樣

是否是方便到爆炸?
當你有一個switch語句,你爲了測試每個case,你都要製造假條件去測試;有一個if…else…語句,你爲了測試不一樣的狀況,你要硬編碼寫了不一樣的狀況,編譯好幾回爲了測試每種狀況……,我想你應該知道爲何本身的頭髮那麼稀疏了。
以上的這些狀況,只需一次編譯,使用lldb的thread命令,僞造返回值,欺騙寄存器,就能夠爲所欲爲的作完全部測試了。

Profile(instruments)


這 個東西怎麼翻譯呢?咱們就叫檢查器吧!!也許已經學習了iOS開發大半年的你,歷來都沒注意到或者使用這個工具,可是博主很負責任的告訴你如今市面上任何 一款出色的APP都會使用instruments來讓代碼更加健壯!難道instrument是春藥?怎麼會使代碼健壯呢?
這個健壯不是那個健壯~哎~~ 我才18歲能不能清純一點呀

instrument 裏面包含了不少工具,內存溢出分析,性能分析,各類分析…… 若是細說的話,這個真的能夠爲每一個工具開一篇博客,可是博主是一個懂得授人以魚不如授人以漁的道理的老司機!因此博主固然不會所有說一遍!咱們就來領着大 家看看專用debug的內存溢出分析工具的使用吧!

靜態分析Analyze

一、靜態分析Analyze:靜態分析不須要運行程序,就能檢查到存在內存泄露的地方。

二、靜態分析的使用方法

(1)Produce -> Analyze   

(2)Command + Shift + B

三、常見的泄漏問題和解決泄漏方式有如下幾種:

(1)Xcode提示信息:Potential leak of an object stored into 'reachability'。翻譯一下:reachability對象的內存單元有潛在的泄露風險。思考:調用了讓某個對象引用計數加1的函數,但沒有調用相應讓其引用計數減1的函數。爲何ARC下會出現內存泄漏?解決:在arc模式下 不是什麼東西 均可以釋放,例如 C-types的對象,都須要手動來進行釋放。全部找到對應的release方法便可。

(2)Xcode提示信息:Returning 'self' while it is not set to the result of '[(super or self) init...]'。翻譯一下:當沒有值時返回self。

#warning 靜態分析疑似有問題的代碼
if ([super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
   [self setSelectionStyle:UITableViewCellSelectionStyleNone];
}
return self;

#warning 代碼修改成以下便可:
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
   [self setSelectionStyle:UITableViewCellSelectionStyleNone];
}else {
   return nil;
}
return self;

(3)Xcode提示信息: The 'viewWillAppear:' instance method in UIViewController subclass 'ProductionListViewController' is missing a [super viewWillAppear:] call。翻譯一下:丟失調用父類的方法。解決:補上調用父類的方法

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

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

在使用leaks以前你們能夠試試這個「Analyze」


analyze 能夠快速的發現你的代碼中release的問題,以及繼承過程當中的父類方法缺失等等問題!若是analyze都經過了,那麼就可使用leaks工具。


若是提示某一個對象有側漏的風險,你還能夠這樣彈出側邊的拓展細節


直接點擊方法就能夠直接進入代碼部分了!!
是否是很簡單粗暴呢!固然還不少其餘工具,不過叫作篇幅的東西老是限制人,誒 真蛋疼~真的還想多說點的 
想要更多瞭解instrument 你們能夠看看這篇文章!

啓用視圖調試

問題彷佛與用戶界面有關。運行app過程當中,按下底部的Debug View Hierarchy 按鈕,或者從菜單中選擇Debug > View Debugging > Capture View Hierarchy 來啓動視圖調試。


啓動視圖調試後,Xcode會對應用程序的視圖層次拍一個快照並展現三維原型視圖來探究用戶界面的層級。該三維視圖除了展現app的視圖層次外,還展現每一個視圖的位置、順序和視圖尺寸,以及視圖間的交互方式。

示例工程在Xcode中的三維視圖展現正常,但表視圖單元格彷佛有點太寬了。


暫停應用程序調試並在左側選中Main.Storyboard來修復問題。點擊表視圖並選中Editor > Resolve Auto Layout Issues > Reset to Suggested Constraints.


編譯並再次運行應用程序以肯定用戶界面展現正常。點擊Debug View Hierarchy按鈕更進一步瞭解視圖調試的功能。

視圖調試功能

點擊並拖拽三維渲染圖的任意一邊,可旋轉或者傾斜用戶界面,向左或者向右傾斜可選中某個表視圖。

選中後,Xcode會高亮該視圖,並在會在右邊展現Object 和Size檢查器。查看在跳轉欄頂部並確認UITableView是右邊最後一個項目。

Object 和 Size檢查器包括大量有用的信息。過去開發者須要依賴日誌語句或者斷點來檢查視圖的配置。

打開右邊的Size inspector(規格檢查器),下方是Auto Layout,能夠看到視圖上已經應用了正確的約束。在Object inspector中,咱們能夠檢查所選視圖的屬性。

在Xcode的調試區有9個視圖調試過程當中要用到的按鈕和滑塊兒。
 


從左到右控件排序:

調整視圖間距:調整不一樣視圖間的間距。

展現被剪切的內容:當前展現視圖中被剪切的部分。

展現約束:展現選中視圖的約束。

重置查看區域:將3D渲染透視圖恢復至默認狀態。

調整查看模式:選擇性地展現3D渲染透視圖,好比僅展現內容,僅展現框架以及同時展現內容和框架。

縮小:縮小3D渲染透視圖

恢復:將3D渲染透視圖恢復至默認尺寸。

放大:放大3D渲染透視圖

調整可視視圖範圍:隱藏視圖或展現視圖,一步步解析3D渲染視圖,向左或者向右滑動滑塊兒有相反的效果。

建議花一點時間上手操做下這些空間,並理解各自的用處。

視圖層排序

再次編譯和運行應用程序,並點擊用戶界面底部的"More"標籤。第一眼看去界面看起來還OK,可是它沒有按照開發者的定義準確執行,圖片上的模糊效果沒有展現出來。咱們能夠經過調試視圖層次來更好地肯定問題所在。

向左或者向右拖拽視圖來查看具體狀況,接着將view spacing slider向右拖動。


這樣一來,不一樣視圖間的間距變大了,層次也更加清晰,咱們看到在圖片"下方"還隱藏着另外一個視圖,選中隱藏的視圖,它就是"丟失"的視覺效果視圖。


打開Main.storyboard 並選中Second View Controller Scene。在左側的文檔概覽面板中,展開Second View Controller的視圖對象以查看子視圖的排序。

Xcode在文檔概覽中按照遞升順序堆疊視圖,換句話說,列表頂層的視圖是視圖層次的基礎。

修復問題很簡單。運行時,Blur Effect View隱藏在Sky Image之下,由於它是視圖層次的第一個視圖。在文檔概覽中點擊並拖拽 Blur Effect View,結果會以下圖展現同樣:


再次運行應用程序就能看到模糊效果了。應用程序的用戶界面看起來符合設計的初衷。咱們還能夠查看iOS模擬器的其餘調試功能,看看還完善了其餘什麼地方或功能。

UITest 單元測試

XCTFail(format…)  //生成一個失敗的測試;
XCTAssertNil(a1, format...)  //爲空判斷,a1爲空時經過,反之不經過;
XCTAssertNotNil(a1, format…) //不爲空判斷,a1不爲空時經過,反之不經過;
XCTAssert(expression, format...) //當expression求值爲TRUE時經過;
XCTAssertTrue(expression, format...) //當expression求值爲TRUE時經過;
XCTAssertFalse(expression, format...)  //當expression求值爲False時經過;
XCTAssertEqualObjects(a1, a2, format...)  //判斷相等,[a1 isEqual:a2]值爲TRUE時經過,其中一個不爲空時,不經過;
XCTAssertNotEqualObjects(a1, a2, format...)  //判斷不等,[a1 isEqual:a2]值爲False時經過;
XCTAssertEqual(a1, a2, format...)  //判斷相等(當a1和a2是 C語言標量、結構體或聯合體時使用,實際測試發現NSString也能夠);
XCTAssertNotEqual(a1, a2, format...)  //判斷不等(當a1和a2是 C語言標量、結構體或聯合體時使用);
XCTAssertEqualWithAccuracy(a1, a2, accuracy, format...)  //判斷相等,(double或float類型)提供一個偏差範圍,當在偏差範圍(+/-accuracy)之內相等時經過測試;
XCTAssertNotEqualWithAccuracy(a1, a2, accuracy, format...)   //判斷不等,(double或float類型)提供一個偏差範圍,當在偏差範圍之內不等時經過測試;
XCTAssertThrows(expression, format...)  //異常測試,當expression發生異常時經過;反之不經過;
XCTAssertThrowsSpecific(expression, specificException, format...)  //異常測試,當expression發生specificException異常時經過;反之發生其餘異常或不發生異常均不經過;
XCTAssertThrowsSpecificNamed(expression, specificException, exception_name, format...)  //異常測試,當expression發生具體異常、具體異常名稱的異常時經過測試,反之不經過;
XCTAssertNoThrow(expression, format…)  //異常測試,當expression沒有發生異常時經過測試;
XCTAssertNoThrowSpecific(expression, specificException, format...)  //異常測試,當expression沒有發生具體異常、具體異常名稱的異常時經過測試,反之不經過;
XCTAssertNoThrowSpecificNamed(expression, specificException, exception_name, format...)  //異常測試,當expression沒有發生具體異常、具體異常名稱的異常時經過測試,反之不經過

iOS模擬器調試功能

編譯並運行應用程序,選中模擬器,從 Debug菜單中選擇Color Blended Layers選項。


而後會看到app的用戶界面被紅色和綠色覆蓋,顯示了哪些圖層能夠被疊加覆蓋,以及哪些圖層是透明的。混合層屬於計算密集型視圖,因此推薦儘量地使用不透明的圖層。


蘋果在其文檔(iOS Simulator User Guide)中對此進行了註明,並在表視圖處理上使用了不透明圖層。滾動視圖時會有些表現不大好的地方,一個重要的緣由就是使用了混合圖層,而若是內容背景是不透明層,那麼頁面滾動效果就會很是流暢和平穩。

對於這款應用程序來講,假使用戶有數百個項目要展現,可能會出現滾動性能不一致的狀況。表視圖單元格當前使用的是混合層。因爲視圖控制器的視圖背景是白色,因此無論表視圖單元格使用的是混合層或者不透明層,終端用戶不會覺察到有什麼不同。

打開Main.storyboard並選中To Do list Scene中的表視圖單元格屬性。在屬性檢查器(Attributes Inspector)中,向下滾動Drawing分區並勾選Opaque。


在啓用Color Blended Layers的狀態下編譯並運行應用程序。因爲表視圖單元格如今使用了不透明層,因此會用綠色覆蓋,以指示它們是不透明的。

除了標記圖層外,還有其餘一些有用的功能可幫開發者在iOS模擬器中調試應用。如下是其中一些比較有用的:

Toggle Slow Animations in Frontmost App: 選中模擬器,打開Debug菜單選中Toggle Slow Animations in Frontmost App,該功能能夠下降app中動畫的運行速度,適合調試包含複雜動畫的應用程序。也但是使用快捷鍵Command-T來操做。
Color Copied Images:該選項能夠給繪製時被Core Animation複製的圖片添加藍綠色疊加層。
Color Misaligned Images:若是圖片邊界沒有與目標像素完美對齊,該功能可爲圖片疊加上一層品紅色。若是圖片使用肯定的比例大小繪製,那麼該功能會爲圖片添加一層黃色疊加。
Color Off Screen Rendered:.該選項爲離屏渲染內容添加一個黃色的疊加層。
不少開發者會忽略接入電話時應用狀態欄的設計問題,你能夠經過觸發通話中狀態欄來簡單測試。在iOS模擬器中,從Hardware菜單中選中Toggle In-Call Status Bar。

想查看app如何響應事件,可按下Command-T來啓用slow animations,並按下Command-Y來展現電話接入時的狀態欄。假若你的應用程序使用了導航欄,那麼操做系統會爲你兼顧到這一起。

固然還有Crash的日誌、測試工程、以及強大牛逼哄哄的第三方調試庫等這篇博客沒有涉及到,這是一個遺憾,可是我相信聰明的你會去Google一番的!

 

參考文章:

《The LLDB Debugger》

《About LLDB and Xcode》

《LLDB調試命令初探》

《與調試器共舞 - LLDB 的華爾茲》

《How to Use Instruments in Xcode》

《iOS中的預編譯指令的初步探究》

《自動化測試》

《UITest 單元測試》

相關文章
相關標籤/搜索