Xcode 中自定義 Instruments

0x000 準備

建議先作以下兩件事,若是不想看也沒有關係:
一、看 Creating Custom Instruments,由於當前文章就是看了這個教程草總結的。
二、我實現的代碼在這裏 CustomInstruments, 歡迎下載。
三、下載以後,先看一下 os_log 初體驗 中的簡單例子,瞭解一下 oslogos_signpost 的簡單使用。
四、須要簡單的瞭解一下 CLIPS 語法
當前文章主要是根據 wwdc2018 & 410 中所說起到的內容進行實現,拿不到視頻中的代碼,那就只能本身模擬。git

0x001 建立自定義項目

這個很簡單,直接經過這個界面的指示建立一個 Mac 項目便可。
github


爲了方便管理,我就直接在個人 HGCustomInstruments 項目中直接建立了,在實際開發中,若是這個工具是針對某個項目的話,這個會更方便些。建立以後是這樣的:

HGTick
建立以後僅有一個源文件(.instrpkg),關於 instruments 源文件問題,能夠參考這個討論: Splitting up instruments code

注意: 這是一個 Mac 應用,應該這樣去運行:
數據庫

RunHGTick

運行成功以後,會彈出一個 Instruments 的副本彈框,僅用於當前調試 HGTick 用。在這裏能夠看到具體的信息:


可是目前這個 HGTick 是不可用的,畢竟尚未開始寫代碼。
相關代碼介紹,請看下節分享。

0x010 HGTick

打開文件(HGTick.instrpkg),是這樣的:express


註釋不少,核心的代碼沒有多少。可是基本上能夠知道這個代碼是 XML 格式的,經過不一樣的標籤標示不一樣的功能,若是圖中的 package 的標籤,標示一個包,緊接着是其子標籤: idtitleowner 等等。接下來就要開始寫代碼了:

<!-- 導入 tick 模塊 至關於 `import` -->
<import-schema>tick</import-schema>
複製代碼

具體的含義在註釋中已經有所體現,說明 tick 模塊是系統已經存在的,咱們直接經過 import-schema 標籤進行導入便可。編程

<!-- 開始構建一個 instrument  -->
<instrument>
    <id>com.coderhg.ticksinstrument</id>
    <title>Ticks-IT</title>
    <category>Behavior</category>
    <purpose>tick demo</purpose>
    <icon>Generic</icon>
    
    <!-- 建立一個表, 這個表中使用到了 `tick`  -->
    <create-table>
        <id>tick-table</id>
        <schema-ref>tick</schema-ref>
    </create-table>
    
    <!-- 軌道視圖 -->
    <graph>
        <title>Ticks-GH</title>
        <lane>
            <title>Ticks-LE</title>
            <table-ref>tick-table</table-ref>
            
            <!-- 軌道視圖顯示的數據 -->
            <plot>
                <value-from>time</value-from>
            </plot>
        </lane>
    </graph>
    
    <!-- 詳情視圖 -->
    <list>
        <title>Ticks-LT1</title>
        <table-ref>tick-table</table-ref>
        <column>time</column>
    </list>
    
    <list>
        <title>Ticks-LT2</title>
        <table-ref>tick-table</table-ref>
        <column>time</column>
    </list>
</instrument>
複製代碼

這就是核心實現 Instruments 功能的代碼了,詳細解釋以下:
一、使用了 Instrument 以後依舊須要添加對應的標識、標題等基本信息。
二、須要建立一個對這個自定義的 Instrument 須要有一張對應的表(table),故須要使用 create-table,值得注意的是這個表所須要的數據是直接來自於 tick schema。
三、開始建立一個軌道視圖,這個軌道視圖的數據來自 tick-table 這張表,因爲這張表引用系統的 tick schema,tick 中有一個 time 屬性,因此能夠直接使用這個時間戳字段。
四、詳情視圖,使用 list 標籤主要是在詳情視圖中顯示數據的。這個 list 至關於咱們開發中的 UITableViewtick-table 至關於數據源(dataSource)。
選擇 HGTickjson


運行效果以下:

至此、一個簡單的自定義功能就完成了。總結一下:
一、使用了一個系統已有的 schema,叫 tick
二、而後就是經過 tick 中提供的 time 屬性爲自定義的表格提供數據。
三、並經過軌道視圖與詳情視圖顯示。

注意: 這個例子沒有實際的意義,僅僅是爲了後面的介紹,作一個簡單的鋪墊。api

0x011 HGJSONDecode

這個主要是模擬解析 JSON 字符串的,爲了簡單起見,我就弄一個模擬 JSON 的方法。這個須要對開發的項目代碼有侵入性的。因此將模擬的代碼以下:bash

/// Touch
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    // log 句柄
    let parsingLog = OSLog(subsystem: "com.coderhg.json", category: "JSONDecode")
    
    // 開始
    os_signpost(.begin, log: parsingLog, name: "Parsing", "Parsing started")
    
    // 模擬: Decode the JSON we just downloaded
    let size = jsonDecode()
    
    // 結束
    os_signpost(.end, log: parsingLog, name: "Parsing", "Parsing finished SIZE:%ld", size)
}

/// 模擬事件
func jsonDecode() -> UInt32 {
    // 0 ~ 3.0 秒的時間
    let timeOut = (arc4random()%6)/2
    sleep(timeOut)
    
    // 模擬當前解析字符串的大小
    let size = arc4random()%100 + 10
    return size
}
複製代碼

注意: 這是在咱們開發的代碼,咱們主要是爲了檢測 jsonDecode 方法的,在這裏生成一個隨機數來標識解析所需的時間。
其實,經過以上的代碼已經發現出現了其它的代碼,主要是 os_signpost api。是的,這個就是爲告終合自定義 Instruments 的時候使用的,只能在這裏寫了開始與結束,在自定義中才能有所匹配,才能作更精細的分析。app

接下來就是看看在對應的 HGJSONDecode.instrpkg 文件中的實現。
os-signpost-interval-schema 標籤使用,目的是自定義一個 schema,具體的以下所示:dom

<!-- 能夠理解成一個數據來源 -->
<os-signpost-interval-schema>
    <id>json-parse</id>
    <title>JSON Decode</title>
    
    <!-- 這三個是與項目中一一對應 -->
    <subsystem>"com.coderhg.json"</subsystem>
    <category>"JSONDecode"</category>
    <name>"Parsing"</name>
    
    <!-- 開始匹配-->
    <start-pattern>
        <message>"Parsing started"</message>
    </start-pattern>
    
    <!-- 結束匹配-->
    <end-pattern>
        <message>"Parsing finished SIZE:" ?data-size-value</message>
    </end-pattern>
    
    <!-- 表中的一列 -->
    <column>
        <!-- 助記符標識, 在 graph 與 list 中只認這個標識 -->
        <mnemonic>data-size</mnemonic>
        <title>JSON Data Size</title>
        <!-- 數據的類型 size-in-bytes -->
        <type>size-in-bytes</type>
        <!-- 顯示 data-size 的值  -->
        <expression>?data-size-value</expression>
    </column>
    
    <!-- https://help.apple.com/instruments/developer/mac/current/#/dev66257045 -->
    <column>
        <mnemonic>impact</mnemonic>
        <title>Impact</title>
        <type>event-concept</type>
        <expression>(if (&gt; ?data-size-value 80) then "High" else "Low")</expression>
    </column>
    
</os-signpost-interval-schema>
複製代碼

上面的代碼,憑藉本身的開發經驗,應該是能看懂的,即便是我每一偶告訴你這是另外一門編程語言:CLIPS。自信觀察 start-patternend-pattern 這兩個標籤,就應該明白其含義:主要是獲取項目中JSON 解析的開始與結束的。其中在結束的時候會匹配出項目中的元數據:解析字符的大小。這裏主要使用的就是 CLIPS 語言的變量。接着就是 column, 這個標籤即便爲這個 shema 定義一些字段,在這裏也發現,這裏提到的 schema 很像一個數據庫。其中這個是數據庫中有兩個 key:data-sizeimpact,其中 impact 是由 data-size-value 的值決定的,大於 80 時值是 High, 不然爲 Low
剩餘的部分,就與 HGTick 中的相似了。最終的效果是這樣的:

這樣就是很清楚的看到每次 JSON 解析的開始與結束,以及執行所花的時間。在實際開發中可能還會同時選中其它的調試模塊,好比 Time Profiler內存檢測 等,這樣能很好的全方位的分析當前的運行環境以及運行狀態。

0x1000 HGImageDownload

主要就是模擬圖片下載的例子,項目中的核心代碼,以下:

/// 下載狀態
var loadStatus:HGImageLoadStatus? {
    didSet {
        // 未知
        if loadStatus == .HGImageLoadStatusUnkown {
            return
        }
        
        // ID
        let signpostID = OSSignpostID(log: HGSignpostLog.imageLoadLog, object: self.obj!)
        let address = unsafeBitCast(self.mockData!, to: UInt.self)
        
        // 開始
        if loadStatus == .HGImageLoadStatusIng {
            os_signpost(.begin, log: HGSignpostLog.imageLoadLog, name: "Background Image", signpostID: signpostID, "Image name:%{public}@, Caller:%lu", name!, address)
            return
        }
        
        var status = "finish"
        // 完成
        if loadStatus == .HGImageLoadStatusFinish {
            
        }
        
        // 取消
        if loadStatus == .HGImageLoadStatusCancel {
            status = "cancel"
        }
        
        os_signpost(.end, log: HGSignpostLog.imageLoadLog, name: "Background Image", signpostID: signpostID, "Status:%{public}@, Size:%lu", status, needTime!)
    }
複製代碼

換句話來講,就是模擬圖片下載,而後在上面的代碼中進行 os_signpost 調用。主要是看 HGImageDownload 中的實現。關於這部分,內容相對比較多,可是相比於前面兩個主要多了建模器的構建相關的內容。 這部分,不打算在這裏 copy 代碼,根據上面的介紹經過項目代碼應該很容易能理解。主要關注的標籤是:point-schemamodeleraggregationnarrative,以及 CLIPS 文件 uplicate-call-detection.clp
最終的運行效果是:

List:Downloads

Narrative

關於第二張的詳情視圖顯示有點問題,可是不耽誤學習。

0x1001 總結

主要是圍繞 WWDC2018/410 進行介紹。
一、HGTick 簡單的使用了一下系統的 schema tick 模塊,基本認識了自定義 Instruments 的套路以及基本格式。
二、HGJSONDecode 自定義了一個 schema 從項目中經過 os_signpost 獲取感興趣的信息,獲取元數據最終進行分析以及顯示。
三、經過 HGImageDownload 實現了自定義 Instruments 的高級應用,主要是建模器的構建。

綜上: 經過當前介紹,基本上能知道自定義 Instruments 的流程已經相關應用。

參考文章

使用 Instruments 檢測內存泄漏
WWDC 2018:建立自定義的 Instrument
Xcode 中 Instruments(Time Profiler) 的注意事項

相關文章
相關標籤/搜索