iOS調試之chisel

iOS調試之chisel

Chisel 是一個 LLDB 指令集合,用戶輔助 iOS 應用差錯。

安裝
  1. chisel的安裝需要使用Homebrew,如果還沒有安裝Homebrew,可以使用下面的命令安裝,如果你已經安裝了,可以跳過這一步

    ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

  2. 用Homebrew安裝chisel:

    brew update
    brew install chisel

  3. ~/.lldbinit中添加命令

    touch ~/.lldbinit
    echo "command script import /usr/local/opt/chisel/libexec/fblldb.py" >> ~/.lldbinit
    重啟一下Xcode,安裝完成。

更新

如果你想更新chisel,只需要輸入更新的命令即可。

brew upgrade chisel

命令
Autolayout

autolayout中有一種bug叫Ambiguous Layouts,意思是你設置的約束不足以確定view的位置或大小。比如你只設置了X軸的位置,沒有設置Y軸的位置。
autolayout提供了專門判斷和查找這類問題的方法:

  • hasAmbiguousLayout用于判斷是否存在Ambiguous Layouts.
  • _autolayoutTrace用于查找存在的Ambiguous Layouts。
alamborder

給存在Ambiguous Layouts的view加上border,方便查找哪些View存在問題。
語法:

Syntax: alamborder --color=color --width=width

  • --color/-c: border的顏色,參數為string類型,比如’red’, ‘green’, ‘magenta’等,不設置默認為紅色。
  • --width/-w: border的寬度,參數為CGFloat類型,不設置默認寬度為2。
alamunborder

將alamborder設置的border去掉。
語法:

Syntax: alamunborder

paltrace

打印某個View的autolayout詳細信息,相當于調用_autolayoutTrace
語法:

Syntax: paltrace <view>

  • <view>: 需要打印詳細信息的view,不傳參數默認為keyWindow
    (lldb) paltrace

      ?UIWindow:0x7f971283fa80
      |   ?UIView:0x7f9710d8f450
      |   |   *UIButton:0x7f9710d8f820'Comst'
      |   |   |   UIButtonLabel:0x7f971283ae90'Comst'
      |   |   *_UILayoutGuide:0x7f9710d913a0
      |   |   *_UILayoutGuide:0x7f9710d92100
      
      Legend:
          * - is laid out with auto layout
          + - is laid out manually, but is represented in the layout engine because translatesAutoresizingMaskIntoConstraints = YES
          ? - layout engine host
    
print

在LLDB中,我們執行的最多的可能就是打印操作了,chisel專門為這類操作封裝了一些打印命令。

pviews

循環打印view層級,正常情況下等效于調用recursiveDescription命令
// 下面2條命令等效
(lldb) po [self.view recursiveDescription]
(lldb) pviews self.view
語法:

pviews [--up] [--depth=depth] <aView>

  • --up/-u: 以view為起始位置,向上打印,直到打印到window層。
  • --depth/-d: 傳入int類型,表示打印的層數,0表示沒有限制。
    e.g: 打印一下self.view層級:
    lldb) pviews self.view
    <UIView: 0x7f9710d8f450; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7f9710d8eda0>>
    | <UIButton: 0x7f9710d8f820; frame = (37 49; 45 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7f9710d8e940>>
    | | <UIButtonLabel: 0x7f971283ae90; frame = (0 6; 45 18); text = 'Comst'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7f97128170a0>>
    | <_UILayoutGuide: 0x7f9710d913a0; frame = (0 0; 0 20); hidden = YES; layer = <CALayer: 0x7f9710d917b0>>
    | <_UILayoutGuide: 0x7f9710d92100; frame = (0 667; 0 0); hidden = YES; layer = <CALayer: 0x7f9710d92290>>
    (lldb) pviews --up self.view
    <UIWindow: 0x7f971283fa80; frame = (0 0; 375 667); autoresize = W+H; gestureRecognizers = <NSArray: 0x7f9712840c00>; layer = <UIWindowLayer: 0x7f971283c2d0>>
    | <UIView: 0x7f9710d8f450; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7f9710d8eda0>>
pvc

循環打印viewController的層級。
語法:

Syntax: pvc <aViewController>

<aViewController>: 表示要打印的viewController,不傳參數默認viewController為當前的VC。
e.g: 打印一下當前VC
(lldb) pvc
<UINavigationController 0x7fe2a2813800>, state: appeared, view: <UILayoutContainerView 0x7fe2a409bb30>
| <ViewController 0x7fe2a2428450>, state: appeared, view: <UIView 0x7fe2a409c660>

pclass

循環打印class的繼承關系。
語法:

Syntax: pclass <object>
<object>: 要打印繼承關系的對象。
e.g: 打印一個View對象的繼承關系
(lldb) pclass [UIButton new]
UIButton
| UIControl
| | UIView
| | | UIResponder
| | | | NSObject

presponder

打印響應鏈。
語法:

Syntax: presponder <startResponder>
<startResponder>: UIResponder對象,響應鏈開始位置。
e.g: 打印一個button的響應鏈
pclass self.btn
UIButton
| UIControl
| | UIView
| | | UIResponder
| | | | NSObject
(lldb) presponder self.btn
<UIButton: 0x7fb16342f610; frame = (65 226; 45 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7fb163410fc0>>
| <UIView: 0x7fb163425810; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fb16341de90>>
| | <ViewController: 0x7fb163524a60>
| | | <UIViewControllerWrapperView: 0x7fb165900050; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fb163721990>>
| | | | <UINavigationTransitionView: 0x7fb16371cf70; frame = (0 0; 375 667); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x7fb163712920>>
| | | | | <UILayoutContainerView: 0x7fb163715e40; frame = (0 0; 375 667); autoresize = W+H; gestureRecognizers = <NSArray: 0x7fb16371f1d0>; layer = <CALayer: 0x7fb16370b2e0>>

   |    |    |    |    |    | <UINavigationController: 0x7fb164022a00>
   |    |    |    |    |    |    | <UIWindow: 0x7fb163533440; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x7fb163534680>; layer = <UIWindowLayer: 0x7fb1635241b0>>
   |    |    |    |    |    |    |    | <UIApplication: 0x7fb163700aa0>
   |    |    |    |    |    |    |    |    | <AppDelegate: 0x7fb163709d10>
ptv

打印屏幕中顯示的tableView,主要是與pcells聯合使用。如果有多個tableView,打印View層級中最上面的一個。
語法:

Syntax: ptv
e.g: 看看當前最上面是哪個tableView
(lldb) ptv
<UITableView: 0x7fde52811800; frame = (0 0; 414 736); clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x7fde526418d0>; layer = <CALayer: 0x7fde5260adc0>; contentOffset: {0, -64}; contentSize: {414, 176}>

pcells

打印tableView中當前可見的cell,如果有多個tableView,打印View層級中最上面的tableView的可見cell。
語法:

Syntax: pcells
e.g: 看看當前可見的cell有哪些
(lldb) pcells
<__NSArrayI 0x7fde52565a00>(
<UITableViewCell: 0x7fde52551180; frame = (0 0; 414 44); text = 'BasicViewController'; autoresize = W; layer = <CALayer: 0x7fde52537140>>,
<UITableViewCell: 0x7fde5255bea0; frame = (0 44; 414 44); text = 'DateViewController'; autoresize = W; layer = <CALayer: 0x7fde5255b1a0>>,
<UITableViewCell: 0x7fde5255e2d0; frame = (0 88; 414 44); text = 'PPTViewController'; autoresize = W; layer = <CALayer: 0x7fde5255e270>>,
<UITableViewCell: 0x7fde5255fce0; frame = (0 132; 414 44); text = 'TableViewController'; autoresize = W; layer = <CALayer: 0x7fde5255fa90>>
)

pinternals

打印一個對象內部的成員變量,這個方法我一般用來看model屬性。
語法:

Syntax: pinternals <object>

  • <object>: 需要打印內部成員變量的對象
    e.g: 我們來看看一個model內部屬性的值
    (lldb) pinternals model
    (Model) $5 = {
    _name = 0x000000010dd1c0a0 @"老鼠愛大米"
    _URL = nil
    _array = nil
    _dictionary = nil
    _string = nil
    _model = nil
    }
pdata

對編碼過的NSData進行解碼打印,等效于調用-[NSString initWithData:encoding:]
語法:

Syntax: pdata [--encoding=encoding] <data>

  • <data>: 需要打印的data,NSData類型。
  • --encoding/-e: 編碼類型,如果缺省默認為utf8,主要支持的類型有:
    - ascii,
    - utf8,
    - utf16, unicode,
    - utf16l (Little endian),
    - utf16b (Big endian),
    - utf32,
    - utf32l (Little endian),
    - utf32b (Big endian),
    - latin1, iso88591 (88591),
    - latin2, iso88592 (88592),
    - cp1251 (1251),
    - cp1252 (1252),
    - cp1253 (1253),
    - cp1254 (1254),
    - cp1250 (1250),
pkp

通過-valueForKeyPath:打印key path對應的值。
語法:

Syntax: pkp <keypath>

  • <keypath>: 需要打印的路徑,如self.view。
pivar

打印對象成員變量。
語法:

Syntax: pivar <object> <ivarName>

  • <object>: id類型,要打印成員變量的對象。
  • <ivarName>: 成員變量的名稱,注意:如果是屬性,對應成員變量的名字默認有_前綴。
Find

debug的時候,我們經常需要查找一些東西,比如View,viewController等。

fvc

根據viewController的Class名字查找VC。
語法:

Syntax: fvc [--name=classNameRegex] [--view=view]

  • --name/-n: string類型參數,根據viewController的Class名字查找viewController。
  • --view/-v: UIView類型參數,根據viewController擁有的view查找viewController。
    說明:上面2個option不能同時使用,只能使用某一個。
    e.g: 我們先根據名字查找一下VC
    (lldb) fvc --name=viewcontroller
    0x7fd01a90f310 ViewController
    e.g: 如果我們知道VC的view地址,也可以根據view來查找VC
    (lldb) fvc --view=0x7fd0194194d0
    Found the owning view controller.
    <ViewController: 0x7fd01a90f310>
fv

根據view的class名字查找view。
語法:

Syntax: fv <classNameRegex>

  • <classNameRegex>: view的class名稱。
    e.g: 查找一下屏幕上的UILabel
    (lldb) fv uilabel
    0x7fd01a91dc10 UILabel
taplog

將點擊的view打印出來,這個命令對于查找哪個view非常有幫助。
說明:要查看的view必須能接收點擊事件,也就是他的userInteractionEnabled必須為YES才能被找到,UILabel和UIImageView默認userInteractionEnabled為NO。
用法:我們需要先將程序暫停,輸入taplog,程序會自己運行,這時候點擊你需要查看的view,控制臺上就會顯示出你剛剛點擊的view相關信息。
e.g: 我們先將程序暫停,輸入taplog
(lldb) taplog
Process 28421 resuming
程序會自己運行,我們再點擊一個UIButton:
<UIButton: 0x7fe6785284e0; frame = (54 244; 46 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7fe678528a50>>

flicker

將view閃爍一下,以便于查找view的位置。
語法:

Syntax: flicker <viewOrLayer>

  • <viewOrLayer>需要閃爍的view或者layer。
    e.g: 我們來看看self.subView的位置
    (lldb) flicker self.subView
vs

在view層級中搜索view,并顯示出來。
語法:

Syntax: vs <view>

  • <view>:要查找的view
    說明:相比fv,vs主要用于顯示view在屏幕上的位置,2個命令可以配合使用
    e.g: 假設我們要找屏幕上的一個view。
    首先用fv查找UIView類型的view:
    (lldb) fv uiview
    0x7fbcf37228d0 UIView
    0x7fbcf3725e90 UIView
    然后看看這2個view到底哪個是我們想要找的view
    (lldb) vs 0x7fbcf3725e90

    Use the following and (q) to quit.
    (w) move to superview
    (s) move to first subview
    (a) move to previous sibling
    (d) move to next sibling
    (p) print the hierarchy

    <UIView: 0x7fbcf3725e90; frame = (0 100; 100 100); layer = <CALayer: 0x7fbcf3712a40>>

Display

debug的時候,可能有一小半的工作是跟UI打交道,關于UI顯示上的東西,也有幾個命令。

caflush

刷新UI界面。一般我們用LLDB命令改變UI,UI并不會立即更新,我們需要使用caflush刷新界面。
語法:

Syntax: caflush

e.g: 我們用命令將label的背景色改為紅色
(lldb) fv uilabel
0x7fb3919189d0 UILabel
(lldb) e [((UILabel*)0x7fb3919189d0) setBackgroundColor:[UIColor redColor]]
(lldb) caflush

border

給View或者layer加上border。
語法:

Syntax: border [—color=color] [--width=width] <viewOrLayer>

  • --color/-c: 邊框顏色,string類型,比如:’red’, ‘green’
    ‘magenta’等,不設置默認為紅色。
  • —width/-w: 邊框寬度,不設置默認為2。
  • <viewOrLayer>: 需要設置邊框的view或者layer。
    e.g: 給剛剛的label加上邊框。
    (lldb) fv uilabel
    0x7fe713901f10 UILabel
    (lldb) border 0x7fe713901f10
unborder

去掉view或者layer的border。
語法:

Syntax: unborder <viewOrLayer>
e.g: 將剛剛加上的border去掉。

(lldb) unborder 0x7fe713901f10
mask

給view添加一個半透明的矩形mask,用來查看view的位置。
語法:

Syntax: mask [--color=color] [--alpha=alpha] <viewOrLayer>

  • --color/-c: mask的顏色,string類型,比如:’red’, ‘green’,’magenta’等,不設置默認為紅色。
  • --alpha/-a: mask的透明度,不設置默認為0.5。
  • <viewOrLayer>: 需要添加mask的view或者layer。
    e.g: 假如label是隱藏的,我們給他添加一個mask,看看他的位置在哪兒
    (lldb) fv uilabel
    0x7fe713901f10 UILabel
    (lldb) mask 0x7fe713901f10
unmask

將添加的mask去掉。
語法:

Syntax: unmask <viewOrLayer>

  • <viewOrLayer>: 需要去掉mask的view或者layer。
    e.g: 我們將剛剛添加的mask去掉
    (lldb) unmask 0x7fe713901f10
    使用命令之后,我們可以看到什么都沒有了,因為label是hidden的。
show

顯示一個view或者layer,相當于執行view.hidden = NO。
語法:

Syntax: show <viewOrLayer>

  • <viewOrLayer>: 需要顯示的view或者layer。
hide

隱藏一個view或者layer,相當于執行view.hidden = YES。
語法:

Syntax: hide <viewOrLayer>

Preview

預覽功能,幫助我們用命令查看一個view或者圖片的真正樣子。

visualize

用預覽App打開UIImage, CGImageRef, UIView, CALayer等對象。
語法:

Syntax: visualize <target>

  • <target>: 需要預覽的對象,id類型
    e.g: 我們來看看某個image的樣子
    (lldb) visualize image
wivar

為對象的成員變量設置watchpoint。
語法:

Syntax: wivar <object> <ivarName>

  • <object>: 需要為成員變量設置watchpoint的對象。id類型
  • <ivarName>: 成員變量的名字,注意一般屬性對應的成員變量帶有_前綴
    e.g: 為self.subView設置watchpoint
    (lldb) wivar self _subView
    Remember to delete the watchpoint using: watchpoint delete 1
bmessage

根據方法名設置斷點
語法:

Syntax: bmessage <expression>

  • <expression>: 設置斷點的方法名,如: -[MyView setFrame:], +[MyView awesomeClassMethod], -[0xabcd1234 setFrame:]等。
    說明:一般設置斷點,如果這個方法本類沒有實現,是父類實現的,斷點是無效的。bmessage有效避免了這種缺陷,即使本類沒有實現,也能設置上斷點。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,156評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,401評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,069評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,873評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,635評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,128評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,203評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,365評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,881評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,733評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,935評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,475評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,172評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,582評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,821評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,595評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,908評論 2 372

推薦閱讀更多精彩內容

  • 不玩LLDB,不知道chisel有多強大。chisel之于LLDB,就像iPhone之于手機,前者幾乎給后者重新下...
    小笨狼閱讀 9,579評論 15 78
  • 1.安裝Chisel源碼地址: ChiselChisel 使用 homebrew 來安裝,如果你沒有安裝homeb...
    代碼干貨閱讀 985評論 0 1
  • LLDB的Xcode默認的調試器,它與LLVM編譯器一起,帶給我們更豐富的流程控制和數據檢測的調試功能。平時用Xc...
    CoderSC閱讀 1,374評論 0 2
  • iOS調試之LLDB Xcode內嵌了LLDB控制臺,在Xcode代碼編輯區的下方。shift + cmd + y...
    comst閱讀 1,497評論 0 3
  • 年關將近,像我這樣的大齡未婚男女又開始不淡定了,越來越怕回家,越來越怕跟父母溝通,而自己其實也越來越不自信了,開...
    米路閱讀 406評論 0 0