coding 的演示功能不讓用,原來搭建的博客訪問不了了。索性將全部博客遷移到簡書,這篇是舊文章,歡迎大家以后來簡書看我的博客
昨天發現微博的圈子里iOS學習氛圍比較好,所以特意注冊了一個新浪微博。無意中在微博里看到了@沒故事的卓同學
的文章Xcode7中你一定要知道的炸裂調試神技,介紹Xcode7中新增了AddressSanitizer工具可以捕獲EXC_BAD_ACCESS。然而Xcode中不是已經有了Zombie了么?怎么又出來了一個Address Sanitizer,他們有什么區別呢?
AddressSanitizer VS Zombie
原理
-
zombie
:
zombie的原理是用生成僵尸對象來替換dealloc的實現,當對象引用計數為0的時候,將需要dealloc的對象轉化為僵尸對象。如果之后再給這個僵尸對象發消息,則拋出異常,并打印出相應的信息,調試者可以很輕松的找到異常發生位置。 -
AddressSanitizer
:
AddressSanitizer的原理是當程序創建變量分配一段內存時,將此內存后面的一段內存也凍結住,標識為中毒內存。如圖所示,黃色是變量所占內存,紫色是凍結的中毒內存。
當程序訪問到中毒內存時(越界訪問),就會拋出異常,并打印出相應log信息。調試者可以根據中斷位置和的log信息,識別bug。如果變量釋放了,變量所占的內存也會標識為中毒內存,這時候訪問這段內存同樣會拋出異常(訪問已經釋放的對象)。
適用性
了解原理之后我們可以大概猜到Zombie和AddressSanitizer的適用性,不過一切還得以實驗結果為準:
實驗后發現AddressSanitizer比Zombie擁有更強大的捕獲能力,特別是在malloc對象和內存越界方面,zombie幾乎無能為力。如果在debug的時候無法捕獲異常,上線之后crash log中概率性的EXC_BAD_ACCESS簡直是一種災難。
缺陷
上面研究發現AddressSanitizer比zombie更有優勢,那么AddressSanitizer有什么缺陷呢?
- AddressSanitizer可能會沒有log,不過會在訪問中毒內存的代碼處斷住,這倒是對debug影響不大
- 使用AddressSanitizer除了分配對象的內存之外,還需要額外的內存,這會導致App內存大量增加,用起來有可能會比較卡
雖然AddressSanitizer有一些缺陷,但是總的來說AddressSanitizer還是一個非常好用的debug工具。
AddressSanitizer使用
在了解AddressSanitizer的功能之后,我們來看看AddressSanitizer用。
AddressSanitizer的使用其實非常簡單,在Xcode上方選擇設備的地方,點擊工程名字,選擇Edit Scheme.
在Diagnostics中選中enable address sanitizer即可。
AddressSanitizer開啟之后,在debug過程中,如果遇到EXC_BAD_ACCESS的問題,Xcode會自動中斷,拋出異常
其他compiler flags
實際AddressSanitizer很早以前就有了,只是沒在Xcode中集成而已。除了AddressSanitizer還有很多其他的compiler flags,undefined-trap就是其中的一種。undefined-trap的功能也非常強大,它可以檢測出程序中的不明確行為,如數據溢出等。
下面我們以undefined-trap舉例,看看怎么用其他的compiler flags:
在Build Settings中的Custom Compiler Flags下為other C Flags添加-fsanitize=undefined-trap -fsanitize-undefined-trap-on-error
完成undefined-trap的設置之后,當程序的數據發生溢出行為時,系統就會拋出異常。
End
經過ARC的洗禮之后,普通的訪問釋放對象產生的EXC_BAD_ACCESS已經大量減少了,現在出現的EXC_BAD_ACCESS有很大一部分來自malloc的對象或者越界訪問。簡單的敵人已經被干掉,剩下的都是難纏的對手了。還好Apple給我們升級了裝備,以后遇到EXC_BAD_ACCESS應該不用那么心驚膽戰了吧?
Reference
Xcode7中你一定要知道的炸裂調試神技
在Xcode 7上直接使用Address Sanitizer
Clang 3.8 documentation