Xcode 8 的 Debug 新特性 —- WWDC 2016 Session 410 & 412 學習筆記

發表於2016-11-19
11406302-e82af8ddd45b88f2

Contents

  • OverView
  • Static Analyzer
    • Localizability
    • Instance Cleanup
    • Nullablility
  • Runtime Issue
  • View Debugging Enhancements
    • Enhancements
    • Debug Workflow
  • Memory Graph Debugging
    • How to use
    • .memgraph file
  • Sanitizer
    • Address Sanitizers ( ASan )
    • Thread Sanitizers ( TSan )
  • Recap
  • Reference Material

1. Overview

本文針對 WWDC 2016 Session 410 和 412 以及 WWDC 2015 Session 413 中的內容進行了整理.

文中主要傾向於如何使用,以及這些工具的使用場景, 如果想了解這些工具的工作原理或者細節方面的東西,請大家觀看原視訊即可.具體連結在文中最後的參考資料裡.

廢話少說,接著往下看吧.

2. Static Analyzer

Static Analyzer 是一個常見的 debug 的工具, 蘋果工程師在 WWDC 中是這樣介紹它的:

  • 不需要 running code (unlike sanitizers)
  • 在處理 edge-case 的 bugs 時, 有著優異的表現
  • 支援 C, C++ 和 Objective-C

2.1 如何使用 Static Analyzer

使用 Static Analyzer 很簡單, 你可以通過選擇 Product -> Analyze 或者 Cmd + Shit + B 的方式執行, 如果有錯誤,就會在 Issue Navigator 上顯示出來

12406302-158702dc7f7ba131

在今年的 Session 412 中, Apple 的工程師告訴我們在 Xcode 8 中, Static Analyzer 能夠檢測出三種新的錯誤, 它們分別是:

  • Localizability
  • Instance Cleanup
  • Nullability

看英文有點不好理解,不用擔心,接著往下看,我們們一個個的說.

2.2 Localizability

Localizability 其實說的是 Static Analyzer 現在能夠檢測出本地化資訊缺失的問題,目前能夠檢測出來兩種型別的錯誤, 一種是沒有使用 NSLocalizeString 這樣的 API, 而直接給控制元件設定 Sting 的情況, 一種是使用了相應的 API, 但在 comment 資訊裡面賦值為 nil. 如果有錯, 就會像下圖一樣, 在程式碼下方出現一個藍色提示條, 告訴開發者具體的錯原因.

13406302-c58b5d9a7b6322ea

在 Xcode 裡,檢測第二種型別的錯誤並不是預設開啟的,如果想開啟,需要在BuildSting 中進行如下設定:

14406302-550e697f682e7cee

Instance Cleanup

Instance Cleanup 說的是什麼呢?

這說的是在 MRC 的程式碼中, 尤其在 dealloc 中,我們不應該對 assign 型別的屬性進行 release 操作,應該對 retain 或者 copy 型別的屬性進行 release 操作, 如果不這樣操作的話,會引發一些不必要的麻煩. 不過現在有了 Xcode 8, 這些問題就交給 Static Analyzer 吧,它能夠很準確的檢測出這樣的錯誤.

15406302-fa9f60b1b30c5280
 16406302-db5e7986ffee3462

Anyway, 還是建議你把程式碼轉成 ARC 吧! 不知道怎麼轉, 看下圖

17406302-2473645f7840b0cd

Nullability

關於這個話題是說的什麼呢?

首先我們得先說說在 2015 年的 WWDC 大會上, Objective-C 引入的一個新特性就叫做 Nullability, 用於表明一個東西到底可以為 nil 還是不可以為 nil , 這和 Swift 裡的 option 型別很相似. 既然知道了這個玩意後,我們再說說 Static Analyzer 在這一塊到底能夠乾點什麼?

通俗的說, Static Analyzer 可以檢測出在不同場景下是否做到了 nullability 的一致性.

那麼我們一般什麼時候會出現 nullability 方面的錯誤呢?

  • Objective-C 與 Swift 混編的場景
  • 在程式碼中有一些邏輯錯誤
  • 不正確的註釋

我想看到這,很多朋友都會對這個功能嗤之以鼻, 並且想著”我只要不使用與 nullability 相關的關鍵字, Static Analyzer 就肯定不會報錯啦.”, 確實從某種角度來說, 你這麼幹以後, Static Analyzer 確實不會報錯了,但這樣真的好麼?

這就回歸到為什麼我們需要使用與 nullability 相關的關鍵字這個問題上, 我認為主要的原因有三個:

  • 便於跟 Swift 的互動
  • 方便使用者明白開發者的意圖
  • 能夠將一些不必要的問題提前到編碼階段, 而不是到使用者使用時才暴露

估計有的朋友會對我的第三個觀點不太理解, 不用在這裡糾結, 下面的這個例子會解釋我的想法.

首先看這段程式碼, 我們假設他的使用場景如下, 這是一個類似地理位置的抽象類, 對於這樣的類,它可以有一個方法來描述它所在的城市或者國家, 這個方法看起來是沒有任務錯誤的, 但其實裡面是有缺陷的, 現在假設我們在大西洋的某個不知名的海域中, 由於這個地方既不屬於某個城市, 更不屬於某個國家, 那麼由於 name 的初始值為 nil , 那麼他的返回值一定為 nil, 這就與 API 設計者規定的 nonnull 相互衝突了, 萬幸的是 Static Analyzer 幫我們檢測到了這一切.

18406302-22dcbaab6ed3535e

但假如我們沒有使用 nonnull 關鍵字呢? 那麼這段話本來是要用於展示在某個 label 上的,但由於返回值為空, 螢幕上空空如也, 使用者好幾臉懵逼, PM 和 QA 的同事火速殺到你的工位前……
總之,不用我說,你應該能明白我意思了, 這就是我說的:

能夠將一些不必要的問題提前到編碼階段, 而不是到使用者使用時才暴露

我們再來看一個例子說明下不正確的巨集註釋產生的問題,在NS_ASSUME_NONNULL_BEGINNS_ASSUME_NONNULL_END 之間的屬性都會被預設為 nonnull 型別, 那麼看下面的程式碼:

19406302-539aae6d5b2a6fa8

在日常的工作中,我們經常是從某個人手裡接過來一段程式碼進行開發, 假設在這個檔案裡, 由於整個程式碼已經到了近 1000 行, 且有好幾個類在同一個 .m 檔案中, 所以兩個巨集寫的非常隱蔽, 你根本沒有察覺到它們的存在.

然後你很自信的宣告瞭一個再普通不過的的 pressure 屬性, 並重寫了它的 get 方法, 同時我們的邏輯很清楚, 這是一個人擁有的方法, 如果他有壓力計的話就測量一下壓力,並返回這個壓力值,如果沒有壓力計的話,就返回 nil , 這個邏輯看起來是如此的正確, 但你一執行就 crash , 是不是很崩潰.

好在 Static Analyzer 告訴了我們問題的關鍵, OK, 這個 bug 很快就能解決了.

Runtime Issue

說完了Static Analyzer, 我們來說說 Runtime Issue 這個東西,就像下面這個圖展示的一樣, 你可以認為以後見到這個紫色的感嘆號標誌就是一個 runtime issue , 哦, 順道說一下左邊的兩個分別是 Error 和 Warnning 狀態, 右邊的兩個分別是 Static Analyzer Issue 和 UI Test Failed 的狀態, 不同於其餘這些東西的出現時間, runtime issue 是出現在程式執行期間的

20406302-459dffa8ea0a5fbe

目前支援 Runtime Issue 的工具有三個, 分別是 Debug View Hierarchy , Thread Sanitizer 和 Debug Memory Graph , 我們會在下面的話題一個個介紹給大家!

嗯吶, 總之, Runtime Issue 的話題就告一段落啦.

View Debugging Enhancements

View Debugging 到底指的是什麼呢? 我想各位看英文時候可能有點懵逼, 但看完下面兩張圖是不是瞬間明白我在說神馬了!!!

21406302-9e1ffd2219f9aff4
 22406302-5fab644961acc4e5

嗯吶, 就是這個功能叫做 View Debugging, 也可以叫做 Debug View Hierarchy, anyway, 你喜歡啥就叫啥吧,

Enhancements

這個功能在 Xcode 6 就有了, 那麼在 Xcode 8 上又有了哪些提升呢? 蘋果工程師給出答案是這樣的,

  • up to 70% faster snapshots
  • layout and transform accuracy
  • blur rendering
  • navigator filtering
  • jump to class
  • auto layout debugging

至於第一個改進點,大家可能需要自己去感受,我也確實沒有興趣做一個資料對比, 畢竟 PM 還找我做需求呢…
那麼我們說下後面五個改進點吧.

layout and transform accuracy

這到底是說神馬呢? 難道是說以前的 layout 和 transform 不準確麼? No, No, No, 並不是說以前不準確,而是說現在比以前更精確了, What, 我說的話是不是好繞,還是直接上圖吧…

23406302-63827dd949eb6d04
24406302-7a401cbfac0753b3
25406302-ec0a20f3b9b1f556

所以說你看出來神馬名堂了麼? 在 Xcode 8 裡能夠更加精確的表明這些約束的意義, 例如是否是等比例縮放(第一張的比例值), 是否是權值較低的約束(第二張的虛線段), 是否是一個不絕對相等的約束(第三張的小於等於). 這些在 Xcode 7 裡都是沒有體現出來的, 總之通過這些標記, 能夠讓我們更加清晰的瞭解到這些約束的意義, 而不只是一根實線而已

blur rendering

這是說在新的 debug 模式下,我們能夠看到 blur 層了. 是不是很美好

26406302-6504d3f9968a2dfc

navigator filtering

這個東西我覺得還是蠻好使的, 因為原先在 Navigator 裡找某個控制元件時, 真的很難, 尤其在那種結構複雜的介面裡, 就看著自己點著那個三角按鈕一遍又一遍的…

Xcode 8 在今年很好的解決了這些個問題, 我們現在在 Navigator 中可以通過控制元件的記憶體地址來定位, 也可以通過它的類名來定位, 甚至可以使用控制元件中展示的 String 內容來定位.

這樣一來, 找控制元件就變得很 easy 了, 是不是!!! 不信, 你看下面的這張圖.

27406302-0973bdb86577f0c8

jump to class

這個新增的功能充分體現了蘋果工程師的人文關懷, 試想一下, 我們每次在定位到對應的控制元件後, 如果想要修改其 layout 的相關屬性時, 有些人會到左邊的 Project Navigator 中的層級結構裡找對應的.m或者.h檔案, 熟悉快捷鍵的人可能會用 Cmd + Shit

  • O 直接跳轉到對應的類中, 總之,你都得想想這個東西的類名, 並輸入點字元神馬的, 很麻煩.

但在Xcode 8 之後, 我們只需要去 UI 控制元件的 Object Inspector 的介面裡點一下右邊深灰色的前進按鈕, 嗖的一下,我們就跳轉到了對應類的檔案中

28406302-543685f210b66b46

auto layout debugging

這個功能就要結合之前的 runtime issue 話題了, 廢話少說, 先上個圖給你們瞅瞅.

29406302-cdca1cc161df6c77

我們可以看到,如果我們在佈局控制元件中有錯誤的話, 我們點選 Debug View Hierarchy 後, Xcode 8 就會報出來一堆 Runtime Issue , 這個功能是不是很吊, 以後寫約束再也不怕不怕啦, 畢竟有錯, 我們們就按提示改唄.

Debug Workflow

這一塊的內容並不是某個 Session 裡提到的, 而是我在看這些個 WWDC 後總結出來的, 你可以發現蘋果的工程師在解決這些問題時, 都是有一個套路的, 套路的英文我也不知道是啥, 就用個 workflow 吧.
他們在解決帶有 runtime issue 的問題時, 都會遵循這樣一個解決思路

  1. Activity Viewer : 檢視 Activity Viewer 上是否有錯誤提示
  2. Issue Navigator : 在 Issue Navigator 上初步瞭解錯誤的型別
  3. Debug Navigator : 在這些介面上了解其層級結構, 呼叫順序, 堆疊資訊, 物件持有的層級結構圖等資訊
  4. Inspector : 檢視具體的細節,並分析錯誤的原因
  5. Source code : 使用 jump to class 功能進入原始碼,並修改

至於這個東西, 我覺得可能需要大家自己在實踐中慢慢體會, 才會更深入的理解為什麼會有這些 debug 工具的產生和為什麼他們要在這裡提示.
當然這也是個仁者見仁,智者見智的問題, anyway 你若安好, 便是晴天!

Memory Graph Debugging

講完了 View Debugging Enhancement , 我們來說說今年 Xcode 8 推出的 Memory Graph Debugging.

最近看到很多公眾號和微博都有朋友在說這個特性, 我在這裡就不花費太多的篇章去講它, 更多的說說我覺得在其他文章裡沒提到的東西吧.

在說這個東西之前, 不知道大家是否知道以下三個命令, 如果沒有大家不妨在自己的機子上試一試

好吧,假設看到這時, 你已經按照我說的那樣,按使用了上面的幾個命令, 那麼下面我就得告訴你一個真相.

其實 Memory Graph Debugging 就是把這樣的一套東西變成了UI介面而已.

How to use

那麼我們接著說說如何使用它吧, 它的使用方式很簡單, 在 app 執行的時候, 點選 Debug View Hierarchy 按鈕旁邊的 Debug Memory Graph 按鈕即可, 對就是那個三個圓圈兩個線的按鈕.

30406302-48f0377ad7f20e1f

哦, 對了如果你想看到物件的 malloc_history, 記得在 Diagnostic Scheme Tab 頁面裡面選擇 Malloc Stack , 否則你是看不到任何資訊的, 命令列也是如此, 另外, 蘋果的工程師還說如果勾選了 Malloc Scribble, 整個結果會更加精確

31406302-3caa56c7083929c8

那麼我們來看看點選 Debug Memory Graph 按鈕後的效果吧

32406302-c2b4008a1d674a45

通過這段時間的使用呢, 大致總結出來這樣的一些規律

  • 綠色的一般都是 UIKit 控制元件及其子類
  • 藍色一般 NSObject 類及其子類
  • 黃色一般都是容器型別及其子類
  • 灰色括號是指 block

當然還有很多一些其他的型別,具體的大家去看右上角的 Memory Inspect 介面就好,上面都會有詳細的資訊

另外這一塊還要跟大家交流的就是在 Session 410 中, 蘋果的工程師說了一些內容, 希望開發者們在使用 Memory Graph Debug Tool 時能夠知道:

  1. 為了避免誤報記憶體洩漏的問題, 蘋果在展示 Memory Graph 時, 增加了一些引用, 這些引用只是為了避免誤報
  2. 在 Memory Graph 所有的強引用都是黑色實線, 而灰色實線並不是弱引用, 只是一些系統級別的引用或者蘋果為了優化顯示效果而新增的, 就像上面第一條說的那樣, 所以在看到灰色的引用時, 就自動忽略它吧
  3. 關於執行效率方面, 蘋果工程師也說了,如果只是為了看 Memory Graph , 在 Malloc Stack 選項中, 直接選擇 Live Allocations Only 即可, 這樣會避免過多的效能消耗
  4. 另外, Swift 3 在 Memory Graph 上的表現要相對較好一些
  5. Sanitizer 與這個功能不能同時開啟, 至於為什麼, 你自己看完這幾個 session 就會明白了

.memgraph file

另外 Xcode 8 專門提供了一個檔案格式來儲存某一時刻 app 的 Memory Graph, 當然這個檔案你是沒法 run 起來的, 它只是個graph, 你要明確這一點.

對於喜歡命令列的小夥伴來說, 蘋果還提供了一下的操作指令

Sanitizer

What does Sanitizer mean?

Santize在英文裡面有美化, 優化的意思, 可想而知 Sanitizer 就是一個用於優化的工具.
那麼 Xcode 中的 Sanitizer 到底是什麼呢? 在 WWDC 2015
Session 413 中, 蘋果的工程師給出以下條目來介紹 Sanitizer:

  • Find bugs at run time
  • Similar to Valgrind
  • Low overhead
  • Work with Swift 3 and C/C++/OC
  • Integrated into Xcode IDE

那麼到底 Sanitizer 在 Xcode 裡怎麼使用呢? 其實很簡單, 開啟 Product -> Scheme -> Edit Scheme, 就會彈出如下的介面, 我們在 Diagnostics 中能夠看到這樣一個標題 Runtime Sanitization, 在它下面有 Address SanitizeThread Sanitizer 兩個選項, 我們只需要勾選相應的 Sanitizer 即可.

33406302-f5937a21f5bc2e2c

說到這裡還必須多說幾句, 此處如果你只是勾選了相應的選項並不代表你就能使用 Sanitizer 來 Check 程式碼了, 你還必須重新 run 一下程式碼, 為什麼呢?

這就必須說說整個程式碼 build flow 了. 如下圖所示, 通過勾選了對應的選項, Xcode 會向 clang 傳遞一個特定的引數, 然後生成一個獨特的 binary, 然後這個 binary 會和 Thread Sanitizer 或者 Address Sanitizer 的 dylib 連結在一起. 這樣 Sanitizer 就實現了它想要達到的功能.

34406302-b3a58fd1cf107857

至於每個 Sanitizer 的實現原理, 我這裡就不過多描述了, 建議大家直接觀看 WWDC 2015 Session 413 ( Address Sanitizer ) 和 WWDC 2016 Session 412 ( Thread Sanitizer ) , 我們這裡還是著重介紹它們的使用方法和使用場景.

總之, 你需要記住的就是, 在使用 Sanitizer 的時候, 要重新 Run 一下程式碼哦.

Address Sanitizer ( ASan )

ASan 其實是 Xcode 在去年新增的一個功能, 它主要用於檢測一些記憶體方面的錯誤, 在 Xcode 8 裡, ASan 已經全面支援了 Swift, 這應該是它唯一新增的一個功能.

那麼 ASan 到底能檢查哪些型別的錯誤呢? 蘋果工程師列舉了以下六種:

  • Use after free
  • Heap buffer overflow
  • Stack buffer overflow
  • Global variable overflow
  • Overflows in C++ containers
  • Use after return

哦對了, 蘋果的工程師還說後面四種是 ASan 獨有的功能, 當然說這話的時候是 2015 年, 不知道 2016 年的時候, 其他的 debug 工具有啥進步沒.

說了這麼多,我們們來看看下面這段程式碼吧.

35406302-9a992567b4d9d912

大家應該能夠看出來如果用 buffer[80] 的話是會產生陣列越界的問題, 雖然 malloc 了 80 個位置,但起始位置是從 0 開始的.

但現實呢, 這段程式碼在不開啟 ASan 的狀況下, 百分之九十九都不會產生 crash , 而且產生 crash 的時候也不會像圖中紅色文字那樣明確的告訴你這是一個 Heap buffer Overflow 問題.

這就是 ASan 的作用, 所以如果再遇到記憶體問題, 不用再誠惶誠恐的改完程式碼後使用哎彌陀佛 Cmd + R 大法了, 是不是很 nice!

Thread Sanitizer ( TSan )

最近發現不少公眾號和微博在說 Xcode 8 的新特性時, 都在說 Debug View Hierarchy 和 Debug Memory Graph 的相關內容, 但說實話, 我覺得今年 Xcode 8 最令人興奮的就是新增了 Thread Sanitizer 這個功能, 說真的, 這個功能太有用了, 為什麼呢?

讓我們想想自己在除錯執行緒方面的 bug 時, 有哪些令人記憶深刻的東西:

  • 執行緒方面的 bug 對時間很敏感, 這就導致很多執行緒的 bug 極難復現, 復現都成問題, 還怎麼改 bug
  • 由於執行緒的抽象概念導致在 debug 時候也比一般的 debug 更費勁兒, 這時候總覺自己腦子不夠使
  • 有時候, 由於執行緒引起的 crash 或者 error ,讓我們根本意識不到這其實是執行緒出了問題

相信上面的三點總會有一個讓你刻骨銘心……

那麼我們趕緊說說怎麼開啟 TSan 來幫我們檢查執行緒問題吧. 喂喂, 這個我們們就不再說一遍了吧, 記得看 ASan 章節裡的那個圖片, 在 Address Sanitizer 下面就行 Thread Sanitizer 啦.

至於 Thread Sanitizer 下面的那個 Pause on Issues 的選項就是說, 如果你想一個一個看 runtime issue 就勾選它, 如果你不想這樣, 就不要勾選它, 具體是個神馬感覺, 你自己試試嘍.

如果你喜歡使用 Comman-Line ,那麼請記住下面的程式碼

哦, 還要加一句, TSan 現在只支援 64為 macOS, 以及 64位的 iOS 和 tvOS 的模擬器, 並不支援真機除錯和 watchOS.

那麼 TSan 作為一個能夠檢查執行緒錯誤的工具, 它現在能檢查哪些型別的錯誤呢? 蘋果給出的答案如下:

  • Use of uninitialized mutexes
  • Thread leaks (missing phread_johin)
  • Unsafe calls in signal handlers (ex:malloc)
  • Unlock from wrong thread
  • Data race

那麼我們拿下面的這段程式碼來舉例:

這段程式碼的意思是,我們在 viewDidLoad 方法裡面重新 reset 自己的狀態, 為了防止多個執行緒去訪問同一個 dataArray 屬性, 造成 data race 的狀態, 我們在 resetStatus 的時候需要加鎖, 但當前程式碼中,我們實際上呼叫的是一個沒有初始化的鎖 ( init 方法在 resetStatus 方法下面哦) , 但這段程式碼在實際執行的過程中,百分之九十九也不會出現 crash, 但有了 TSan 後, 我們來看看發生了什麼變化

36406302-9d2a08e5b865d35c

發現 runtime issue 的標誌了麼! 看不清啊,那我們把左邊的 Issue Navigator 放大一下

37406302-a8a4442dbeee8fc5

發現沒有,在 Issue Navigator 中, TSan 明確的告訴了我們錯誤的型別, 而且把執行緒中的歷史資訊都記錄了下來以便我們分析並解決這個問題, 有沒有很貼心!

通過這個問題, 你發現了什麼問題麼?

  • 首先 TSan 並沒有觸發 Crash 並且成功捕獲到了這個執行緒問題, 這說明 TSan 不需要我們去復現 crash 來捕獲錯誤
  • 然後你在不同機型, 不同環境下執行帶有執行緒錯誤的程式, 你會發現每次 TSan 都能捕獲到這個 bug, 這說明 TSan 對時間是不敏感的.

這樣安全可靠的 debug 工具你怎能不愛呢!

說道這, 不妨我們多說一點, 大家都知道 data race 是執行緒中最常出現的問題, 造成 data race 的情況無非就是兩種, 一種是邏輯錯誤, 一種是沒有加鎖. 在這裡我特別想分享一個我自己在使用 TSan 時編寫的小 Demo.

我們先看一下程式碼的使用場景, 我們假設有三個售票員在賣票, 票的數量由 ticketsCount 決定, 同時我們將售票員抽象成一個執行緒類:

然後我們執行下面的這段程式碼

會發生什麼呢? 顯而易見,由於沒有加鎖, 售票員會賣出去不該賣出去的票

38406302-fa93ac20dfdd9071

那我們加個鎖試試?

加鎖後,我們發現售票員確實沒有再賣出去不該賣的票,但是好像只有一個售票員在賣票.

39406302-06af1dd4b1f23cb0

這顯然是一個邏輯錯誤, 我們鎖住更像是執行緒, 而不是資源, 所以我們再改進一下

來看看列印臺的結果

40406302-59117ef3dd49a63b

看起來是不是特別完美! 不同的售票員在賣票, 而且也沒有出現賣出去不該賣的票, 但是其實這段程式碼還是有潛在風險的.

What? What? What? 怎麼會有錯…

這時候我希望你能開啟 TSan 來分析下潛在的風險在哪裡吧,

PS: 如果你比較懶的話, 好好想想 dispatch_async 用的對麼? 如果我在這裡麼加入了一些危險操作 ?

說到這, 你是不是又一次被 TSan 強大的功能震撼住了, 說真的, TSan 真的是 Xcode 8 裡一個非常強大的新功能, 它能夠幫我們察覺到很多很多我們自己完全意識不到的細小問題, 而在這些問題經常會弄得我們在 debug 時候苦不堪言, 所以從今天開始, 在你程式設計的時候, 用一下 TSan 吧

Recap

總結一下今天我們到底說了些什麼:

  • 一種新的檔案格式 : .memgraph
  • 兩個新的概念 : Debug workflowRuntime issue
  • 三類 debug 工具 : Sanitizer, View Hierarchy Debug Tool , Memory Graph Debug Tool

最後呢, 想說的很簡單, Use this tools on our project and Make our app better than ever

Reference Material

WWDC 2016 Session 410 – Visual Debugging with Xcode : https://developer.apple.com/videos/play/wwdc2016/410/

WWDC 2016 Session 412 – Thread Sanitizer and Static Analysis : https://developer.apple.com/videos/play/wwdc2016/412/

WWDC 2015 Session 413 – Advanced Debugging and the Address Sanitizer : https://developer.apple.com/videos/play/wwdc2015/413/

相關文章