Xcode 8 的 Debug 新特性


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 上顯示出來


 

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

  • Localizability
  • Instance Cleanup
  • Nullability

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

2.2 Localizability

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


 

在 Xcode 里,檢測第二種類型的錯誤並不是默認開啟的,如果想開啟,需要在BuildSting 中進行如下設置:


 

Instance Cleanup

Instance Cleanup 說的是什么呢?

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


 

 

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


 

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 幫我們檢測到了這一切.


 

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

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

我們再來看一個例子說明下不正確的宏注釋產生的問題,在NS_ASSUME_NONNULL_BEGINNS_ASSUME_NONNULL_END 之間的屬性都會被默認為 nonnull 類型, 那么看下面的代碼:


 

在日常的工作中,我們經常是從某個人手里接過來一段代碼進行開發, 假設在這個文件里, 由於整個代碼已經到了近 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 是出現在程序運行期間的


 

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

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

View Debugging Enhancements

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


 

 

嗯吶, 就是這個功能叫做 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, 我說的話是不是好繞,還是直接上圖吧...


 

 

 

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

blur rendering

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


 

navigator filtering

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

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

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


 

jump to class

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

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

但在Xcode 8 之后, 我們只需要去 UI 控件的 Object Inspector 的界面里點一下右邊深灰色的前進按鈕, 嗖的一下,我們就跳轉到了對應類的文件中


 

auto layout debugging

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


 

我們可以看到,如果我們在布局控件中有錯誤的話, 我們點擊 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.

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

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

$ heap YourAppName $ leaks YourAppName $ malloc_history YourAppName Address

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

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

How to use

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


 

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


 

那么我們來看看點擊 Debug Memory Graph 按鈕后的效果吧


 

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

  • 綠色的一般都是 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, 你要明確這一點.

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

$ leaks --outputGraph=<path> <process> //creates .memgraph file $ {leaks|vmmap|heap} <path/to/file.memgraph> [options] //operate on .memgraph file

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 即可.


 

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

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


 

至於每個 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 工具有啥進步沒.

說了這么多,咱們來看看下面這段代碼吧.


 

大家應該能夠看出來如果用 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 ,那么請記住下面的代碼

//Compile and Link with TSan $ clang -fsanitize=thread source.c -o executable $ swift -sanitize=thread source.swift -o executable $ xcodebuild -enableThreadSanitizer YES //Stop after the first error $ TSAN_OPTIONS=halt_on_error=1 ./executable

哦, 還要加一句, 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

那么我們拿下面的這段代碼來舉例:

- (void)viewDidLoad { [super viewDidLoad]; [self resetStatue]; pthread_mutex_init(&(_mutex), NULL); } - (void)resetStatue{ [self acquireLock]; self.dataArray = nil; [self releaseLock]; } - (void)acquireLock{ pthread_mutex_lock(&_mutex); } - (void)releaseLock{ pthread_mutex_unlock(&_mutex); }

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


 

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


 

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

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

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

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

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

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

    self.ticketsCount = 100; NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; thread1.name = @"售票員1"; self.thread1 = thread1; NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; thread2.name = @"售票員2"; self.thread2 = thread2; NSThread *thread3 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; thread3.name = @"售票員3"; self.thread3 = thread3;

然后我們執行下面的這段代碼

    for (NSUInteger count = self.ticketsCount; count > 0; count--) { if (count > 0) { [NSThread sleepForTimeInterval:0.1]; NSString *name = [NSThread currentThread].name; dispatch_async(dispatch_get_main_queue(), ^{ self.ticketsCount = count - 1; NSString *string = [NSString stringWithFormat:@"%@賣了一張票, 還剩%zd張票", name, self.ticketsCount]; self.ticketsCountLabel.text = string; NSLog(@"%@", string); }); } else { dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"票賣完了"); }); return; } }

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


 

那我們加個鎖試試?

    @synchronized (self) { for (NSUInteger count = self.ticketsCount; count > 0; count--) { if (count > 0) { [NSThread sleepForTimeInterval:0.1]; NSString *name = [NSThread currentThread].name; dispatch_async(dispatch_get_main_queue(), ^{ self.ticketsCount = count - 1; NSString *string = [NSString stringWithFormat:@"%@賣了一張票, 還剩%zd張票", name, self.ticketsCount]; self.ticketsCountLabel.text = string; NSLog(@"%@", string); }); } else { dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"票賣完了"); }); return; } } }

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


 

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

    while (1) { @synchronized (self) { if (self.ticketsCount > 0) { [NSThread sleepForTimeInterval:0.1]; NSString *name = [NSThread currentThread].name; self.ticketsCount = self.ticketsCount - 1; dispatch_async(dispatch_get_main_queue(), ^{ NSString *string = [NSString stringWithFormat:@"%@賣了一張票, 還剩%zd張票", name, self.ticketsCount]; self.ticketsCountLabel.text = string; NSLog(@"%@", string); }); } else { dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"票賣完了"); }); return; } } }

來看看打印台的結果


 

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

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/



文/SketchK七爺(簡書作者)
原文鏈接:http://www.jianshu.com/p/074072c33916
著作權歸作者所有,轉載請聯系作者獲得授權,並標注“簡書作者”。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM