相信每一個人或多或少都在用LLDB來調試,好比po
一個對象。LLDB的是很是強大的,且有內建的,完整的 Python 支持。今天咱們主要介紹一個 facebook 開源的 lldb 插件 Chisel。可讓你的調試更Easy.python
1.安裝Chisel
源碼地址: Chiselgit
Chisel 使用 homebrew 來安裝,若是你沒有安裝homebrew, 參考 homebrew。github
1
2 |
brew update brew install chisel |
安裝完成按照安裝日誌上的提示,在~/.lldbinit
文件中添加一行,沒有則新建。 提示相似以下:ide
1
2 3 |
==> Caveats Add the following line to ~/.lldbinit to load chisel when Xcode launches: command script import /usr/local/opt/chisel/libexec/fblldb.py |
作好上面的步驟,而後重啓Xcode就能夠嘗試下了。動畫
2.內置命令
Chisel 爲lldb提供了新增的便捷命令,是很是實用的命令ui
2.1 pviews
這個命令能夠遞歸打印全部的view,並能標示層級,至關於 UIView 的私有輔助方法 [view recursiveDescription]
。 善用使用這個功能會讓你在調試定位問題時省去不少麻煩。spa
使用示例:插件
1
2 3 4 5 6 7 8 9 |
(lldb) pviews view <TestView: 0x18df8070; baseClass = UIControl; frame = (144 9; 126 167); layer = <CALayer: 0x18df8150>> | <UIView: 0x18df81d0; frame = (0 0; 126 126); userInteractionEnabled = NO; layer = <CALayer: 0x18df8240>> | <UIImageView: 0x18df8330; frame = (0 0; 126 126); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x18df83b0>> | <UILabel: 0x18df8460; frame = (0 135; 126 14); text = 'haha'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x18df7fb0>> | | <_UILabelContentLayer: 0x131a3d50> (layer) | <UILabel: 0x18df8670; frame = (0 155; 126 12); text = 'hahaha'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x18df8730>> | | <_UILabelContentLayer: 0x131bea10> (layer) | <UIImageView: 0x18df88d0; frame = (0 9; 28 27); hidden = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x18df8ba0>> |
2.2 pvc
這個命令也是遞歸打印層級,可是不是view,而是viewController。利用它咱們能夠對viewController的結構一目瞭然。 其實蘋果在IOS8也默默的添加了 UIViewController 的一個私有輔助方法 [UIViewController _printHierarchy]
一樣的效果。debug
預覽效果:設計
1
2 3 4 5 6 7 8 9 10 11 12 13 14 |
(lldb) pvc <TabBarController: 0x13772fd0; view = <UILayoutContainerView; 0x151b3a30>; frame = (0, 0; 414, 736)> | <UINavigationController: 0x1602b800; view = <UILayoutContainerView; 0x1b00aca0>; frame = (0, 0; 414, 736)> | | <FirstViewController: 0x16029c00; view = <UIView; 0x1b01e1c0>; frame = (0, 0; 414, 736)> | <UINavigationController: 0x138c5200; view = <UILayoutContainerView; 0x1316a080>; frame = (0, 0; 414, 736)> | | <SecondViewController: 0x16030400; view = <UIView; 0x2094b370>; frame = (0, 0; 414, 736)> | | | <SecondChildViewController: 0x15af6000; view = <UIView; 0x18d4e650>; frame = (0, 64; 414, 628)> | <UINavigationController: 0x1383ca00; view = <UILayoutContainerView; 0x13180070>; frame = (0, 0; 414, 736)> | | <ThirdViewController: 0x138ddc00; view = <UIView; 0x18df6650>; frame = (0, 0; 414, 736)> | | | <ThirdChild1ViewController: 0x1393fe00; view = <UIView; 0x131ec000>; frame = (0, 0; 414, 672)> | | | <ThirdChild2ViewController: 0x138dce00; view = <UIView; 0x204075a0>; frame = (414, 0; 414, 672)> | | | <ThirdChild3ViewController: 0x138a8e00; view = <UIView; 0x20426250>; frame = (828, 0; 414, 672)> | <UINavigationController: 0x160eca00; view = <UILayoutContainerView; 0x152f7d90>; frame = (0, 0; 414, 736)> | | <FourViewController: 0x13157cc0; view not loaded> |
是否是方便不少呢,並且還能夠看到 viewController 是否已經 viewDidLoad .
2.3 visualize
這是個頗有意思的功能,它可讓你使用Mac的預覽打開一個 UIImage, CGImageRef, UIView, 或 CALayer。 這個功能或許能夠幫咱們用來截圖、用來定位一個view的具體內容。 可是在我試用了一下,發現暫時仍是隻能在模擬器時使用,真機還不行。
使用簡單:
1
|
(lldb) visualize imageView |
2.4 fv & fvc
fv
和 fvc
這兩個命令是用來經過類名搜索當前內存中存在的view和viewController實例的命令,支持正則搜索。
如:
1
2 3 4 5 6 7 8 9 |
(lldb) fv scrollView 0x18d3b8c0 UIScrollView 0x137d0c50 UIScrollView 0x131b1580 UIScrollView 0x131b2070 UIScrollView (lldb) fvc Home 0x1393fe00 HomeFeedsViewController 0x138a8e00 HomeFeedsViewController (lldb) |
2.5 show & hide
這兩個命令用來顯示和隱藏一個指定的 UIView . 你甚至不須要Continue Progress. 就能夠看到效果。
2.6 mask/umask border/unborder
這兩組命令用來標識一個view或layer的位置時用, mask用來在view上覆蓋一個半透明的矩形, border能夠給view添加邊框。可是在我實際使用的過程當中mask老是會報錯,估計是有bug, 那麼mask/unmask 通常不要用好了,用border命令是同樣的效果,反正兩者的用途都是找到一個對應的view.
2.7 caflush
這個命令會從新渲染,便可以從新繪製界面, 至關於執行了 [CATransaction flush]
方法,要注意若是在動畫過程當中執行這個命令,就直接渲染出動畫結束的效果。
當你想在調試界面顏色、座標之類的時候,能夠直接在控制檯修改屬性,而後caflush
就能夠看到效果啦,是否是要比改代碼,而後從新build省事多了呢。
例, 其中 $122 便是目標UIView:
1
2 3 4 |
(lldb) p view (long) $122 = 140718754142192 (lldb) e (void)[$122 setBackgroundColor:[UIColor greenColor]] (lldb) caflush |
2.8 bmessage
這個命令就是用來打斷點用的了,雖然你們斷點可能都喜歡在圖形界面裏面打,可是考慮一種狀況:咱們想在 [MyViewController viewWillAppear:]
裏面打斷點,可是 MyViewController並無實現 viewWillAppear:
方法, 以往的做法可能就是在子類中實現下viewWillAppear:
,而後打斷點,而後rebuild。
那麼幸虧有了 bmessage
命令。咱們能夠不用這樣就能夠打這個效果的斷點: (lldb) bmessage -[MyViewController viewWillAppear:]
上面命令會在其父類的 viewWillAppear:
方法中打斷點,並添加上了條件:[self isKindOfClass:[MyViewController class]]
3. 自定義命令
咱們也能夠自定義插件,不過前提是要懂一些 python。 好比設計一個打印keyWindow的windowLevel的命令:
建立python腳本文件 /magical/commands/example.py
:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#!/usr/bin/python # Example file with custom commands, located at /magical/commands/example.py import lldb import fblldbbase as fb def lldbcommands(): return [ PrintKeyWindowLevel() ] class PrintKeyWindowLevel(fb.FBCommand): def name(self): return 'pkeywinlevel' def description(self): return 'An incredibly contrived command that prints the window level of the key window.' def run(self, arguments, options): # It's a good habit to explicitly cast the type of all return # values and arguments. LLDB can't always find them on its own. lldb.debugger.HandleCommand('p (CGFloat)[(id)[(id)[UIApplication sharedApplication] keyWindow] windowLevel]') |
其中定義了PrintKeyWindowLevel
的類,須要實現 name
description
run
方法來分別告訴名稱、描述、和執行實體。
建立好腳本後,而後在前面安裝時建立的 ~/.lldbinit
文件中添加一行:
1
|
script fblldb.loadCommandsInDirectory('/magical/commands/') |
而後重啓Xcode以後就可使用自定義的命令啦。
參考文檔:http://blog.cnbluebox.com/blog/2015/03/05/chisel/