MainThreadChecker
一款監控子線程操縱 UI 的能力,也可以添加自定義的 API 進行監控(實現在子線程監控某些 API 的時候捕獲具體堆棧信息,幫助定位問題)
背景介紹
可能有些人一直沒有遇到過因爲在子線程操作 UI,導致在開發階段 Xcode console 輸出了一堆日誌,大體如下
其實我們可以給 Xcode 打個 Runtime Issue Breakpoint
,type 選擇 Main Thread Checker
, 在發生子線程操作 UI 的時候就會被系統檢測到並觸發斷點,同時可以看到堆棧情況
效果如下
問題及解決方案
上述的功能是在 Xcode 自帶的,連接 Xcode 做調試才具備的功能,線上包無法檢測到。
經過探索 Xcode 實現該功能是依賴於設備上的 libMainThreadChecker.dylib
庫,我們可以通過 dlopen
方法強制加載該庫讓非 Xcode 環境下也擁有監測功能。
另外在監控到子線程調用 UI 調用時,在 Xcode 環境下,會將調用棧輸出到控制檯,經過測試,libMainThreadChecker.dylib
使用的是進行輸出的,由於 NSLog 是將信息輸出到 STDERR
中,我們可以通過 NSPipe
與 dup2
將 STDERR
輸出攔截,通過對信息的文案的判斷,進而獲取監測到的 UI 調用,最後可以通過堆棧打印出來,就可以幫助定位到具體問題。
libMainThreadChecker.dylib
庫具有侷限性,僅僅對系統提供的一些特定類的特定 API 在子線程調用會被監控到(例如 UIKit 框架中 UIView 類)。 但是某些類有些 API 我們也不希望在子線程被調用,這時候 libMainThreadChecker.dylib
是無法滿足的。
對 libMainThreadChecker.dylib
庫的彙編代碼研究,發現 libMainThreadChecker.dylib
是通過內部 __main_thread_add_check_for_selector
這個方法來進行類和方法的註冊的。所以如果我們同樣可以通過 dlsym
來調用該方法,以達到對自定義類和方法的主線程調用監測。
另外該功能可以在線下 debug 階段開啓,判斷是否是在 Xcode debug 狀態,可以通過蘋果提供的官方判斷方法實現。
對 dlopen、dlsym 陌生的小夥伴可以直接看 Apple 官方文檔,這裏不做展開。
APM 合集
對於 APM 感興趣的小夥伴可以查看這篇帶你打造一套 APM 監控系統的文章