小笨狼的LLDB技巧:chisel

jopen 4年前

不玩LLDB,不知道chisel有多强大。chisel之于LLDB,就像iPhone之于手机,前者几乎给后者重新下了一次定义。如果你还不知道什么是LLDB,请看我上一篇文章《小笨狼与LLDB的故事》。

安装

安装Homebrew

chisel的安装需要使用Homebrew,如果还没有安装Homebrew,可以使用下面的命令安装,如果你已经安装了,可以跳过这一步

Objective-C

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

用Homebrew安装chisel

搞定Homebrew之后,你就可以用他来安装chisel了

Objective-C

brew update  brew install chisel
brewupdate  brewinstallchisel
</div>

官网的命令里有一个 brew update ,是用来更新Homebrew版本的,如果你是新安装的Homebrew,可以省略掉这条命令

在 ~/.lldbinit 中添加命令

安装好之后,terminal中会出现这个东西

Objective-C

==> Caveats  Add the following line to ~/.lldbinit to load chisel when Xcode launches:    command script import /usr/local/opt/chisel/libexec/fblldb.py
==> Caveats  Add thefollowinglineto ~/.lldbinittoloadchiselwhenXcode launches:    command scriptimport /usr/local/opt/chisel/libexec/fblldb.py
</div>

意思是需要把这个命令 command script import /usr/local/opt/chisel/libexec/fblldb.py 加到 ~/.lldbinit 文件中

我们将命令其添加进去即可。如果你已经添加过一次了,不需要再次添加

Objective-C

touch ~/.lldbinit  echo "command script import /usr/local/opt/chisel/libexec/fblldb.py" >> ~/.lldbinit
touch ~/.lldbinit  echo "command script import /usr/local/opt/chisel/libexec/fblldb.py" >> ~/.lldbinit
</div>

重启一下Xcode,安装完成

更新

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

Objective-C

brew upgrade chisel
brewupgradechisel
</div>

命令

Autolayout

autolayout中有一种bug叫 Ambiguous Layouts ,意思是你设置的约束不足以确定view的位置或大小。比如你只设置了X轴的位置,没有设置Y轴的位置

autolayout提供了专门判断和查找这类问题的方法:

Objective-C

hasAmbiguousLayout. Available for both iOS and OS X. Call this method on a misplaced view. It returns YES if the view’s frame is ambiguous. Otherwise, it returns NO.  _autolayoutTrace. Available as a private method in iOS. Call this method on a view. It returns a string with diagnostic information about the entire view hierarchy containing that view. Ambiguous views are labeled, and so are views that have translatesAutoresizingMaskIntoConstraints set to YES.
hasAmbiguousLayout. Available forbothiOSandOS X. Call thismethodon a misplacedview. It returnsYES if theview’s frameisambiguous. Otherwise, itreturnsNO.  _autolayoutTrace. Available as a privatemethodin iOS. Call thismethodon a view. It returns a stringwithdiagnosticinformationabouttheentireviewhierarchycontainingthatview. Ambiguous viewsarelabeled, andsoareviewsthathavetranslatesAutoresizingMaskIntoConstraintssettoYES.
</div>
  • hasAmbiguousLayout 用于判断是否存在 Ambiguous Layouts
  • _autolayoutTrace 用于查找存在的 Ambiguous Layouts

但是即使有查找的方法,真正去做这个事儿也比较费时费力的,这时候chisel给我们提供了更为方便的命令

alamborder

给存在 Ambiguous Layouts 的view加上border,方便查找哪些View存在问题

语法:

Objective-C

Syntax: alamborder [--color=color] [--width=width]
Syntax: alamborder [--color=color] [--width=width]
</div>
  • --color / -c : border的颜色,参数为string类型,比如’red’, ‘green’, ‘magenta’等,不设置默认为红色。
  • --width / -w : border的宽度,参数为CGFloat类型,不设置默认宽度为2。

e.g: 假设我们写了这么一段代码,可以明显看出,我们没有设置X轴的位置。

Objective-C

UIView *subview = [UIView new];  [self.view addSubview:subview];  [subview mas_makeConstraints:^(MASConstraintMaker *make) {      make.top.offset(100);      make.size.equalTo(@100);  }];
UIView *subview = [UIView new];  [self.viewaddSubview:subview];  [subviewmas_makeConstraints:^(MASConstraintMaker *make) {      make.top.offset(100);      make.size.equalTo(@100);  }];
</div>

运行代码之后,在LLDB控制台输入 alamborder

Objective-C

(lldb) alamborder
(lldb) alamborder
</div>

所有带有 Ambiguous Layouts 的view立即会被渲染上红色border

可以看到,subview的边框已经变为红色,另外只要有一个View存在 Ambiguous Layouts ,UIWindow的边框也会变为红色,这就有效的避免了宽度或者高度为0的 Ambiguous Layouts 不宜察觉的缺陷。

alamunborder

将 alamborder 设置的border去掉.

语法:

Objective-C

Syntax: alamunborder
Syntax: alamunborder
</div>

e.g: 刚刚设置的border,在lldb控制台输入 alamunborder 即可去掉边框

Objective-C

(lldb) alamborder
(lldb) alamborder
</div>

paltrace

打印某个View的autolayout详细信息,相当于调用 _autolayoutTrace

语法:

Objective-C

Syntax: paltrace <view>
Syntax: paltrace <view>
</div>
  • <view> : 需要打印详细信息的view,不传参数默认为keyWindow

e.g: 查看一下keyWindow上有哪个view存在 Ambiguous Layouts

Objective-C

(lldb) paltrace •UIWindow:0x7ff450d2fb50 - AMBIGUOUS LAYOUT | •UIView:0x7ff450e14430 | | _UILayoutGuide:0x7ff450d30e90 | | _UILayoutGuide:0x7ff450d31230 | | *UIView:0x7ff450d32870- AMBIGUOUS LAYOUT for UIView:0x7ff450d32870.minX{id: 46}

Legend: * - is laid out with auto layout + - is laid out manually, but is represented in the layout engine because translatesAutoresizingMaskIntoConstraints = YES • - layout engine host

(lldb) paltrace   •UIWindow:0x7ff450d2fb50 - AMBIGUOUS LAYOUT  |  •UIView:0x7ff450e14430  |  |  *_UILayoutGuide:0x7ff450d30e90  |  |  *_UILayoutGuide:0x7ff450d31230  |  |  *UIView:0x7ff450d32870- AMBIGUOUS LAYOUT forUIView:0x7ff450d32870.minX{id: 46}     Legend:      * - islaidout withautolayout      + - islaidout manually, butisrepresentedin thelayoutenginebecausetranslatesAutoresizingMaskIntoConstraints = YES      • - layoutenginehost
</div>

我们可以看到, UIView:0x7ff450d32870 存在 Ambiguous Layouts ,原因是缺少 minX 。也就是没有设置X轴的位置

Print

在LLDB中,我们执行的最多的可能就是打印操作了,chisel专门为这类操作封装了一些打印命令

pviews

循环打印view层级,正常情况下等效于调用 recursiveDescription 命令

Objective-C

// 下面2条命令等效  (lldb) po [self.view recursiveDescription]  (lldb) pviews self.view
// 下面2条命令等效  (lldb) po [self.viewrecursiveDescription]  (lldb) pviewsself.view
</div>

语法:

Objective-C

pviews [--up] [--depth=depth] <aView>
pviews [--up] [--depth=depth] <aView>
</div>
  • --up / -u : 以view为起始位置,向上打印,直到打印到window层
  • --depth / -d : 传入int类型,表示打印的层数,0表示没有限制

e.g: 打印一下self.view层级

Objective-C

(lldb) pviews self.view  <UIView: 0x7fee7ae1fa60; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fee7ae1d3c0>>     | <UIButton: 0x7fee7ae1dd90; frame = (54 244; 46 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7fee7ae1e300>>     | <UIView: 0x7fee7ae1f2e0; frame = (35 312; 240 128); autoresize = RM+BM; layer = <CALayer: 0x7fee7ae1f660>>     | <_UILayoutGuide: 0x7fee7ae1fc20; frame = (0 0; 0 0); hidden = YES; layer = <CALayer: 0x7fee7ae20030>>     | <_UILayoutGuide: 0x7fee7ae20b30; frame = (0 0; 0 0); hidden = YES; layer = <CALayer: 0x7fee7ae1d100>>     | <UILabel: 0x7fee7ae1d3f0; frame = (0.4 150; 58.25 20.5); text = 'aaa'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fee7ae1bb30>>
(lldb) pviewsself.view  <UIView: 0x7fee7ae1fa60; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fee7ae1d3c0>>    | <UIButton: 0x7fee7ae1dd90; frame = (54 244; 46 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7fee7ae1e300>>    | <UIView: 0x7fee7ae1f2e0; frame = (35 312; 240 128); autoresize = RM+BM; layer = <CALayer: 0x7fee7ae1f660>>    | <_UILayoutGuide: 0x7fee7ae1fc20; frame = (0 0; 0 0); hidden = YES; layer = <CALayer: 0x7fee7ae20030>>    | <_UILayoutGuide: 0x7fee7ae20b30; frame = (0 0; 0 0); hidden = YES; layer = <CALayer: 0x7fee7ae1d100>>    | <UILabel: 0x7fee7ae1d3f0; frame = (0.4 150; 58.25 20.5); text = 'aaa'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fee7ae1bb30>>
</div>

pvc

循环打印viewController的层级

语法:

Objective-C

Syntax: pvc <aViewController>
Syntax: pvc <aViewController>
</div>

<aViewController> : 表示要打印的viewController,不传参数默认viewController为当前的VC

e.g: 打印一下当前VC

Objective-C

(lldb) pvc  <UINavigationController 0x7fce1a03d800>, state: appeared, view: <UILayoutContainerView 0x7fce19500c00>     | <ViewController 0x7fce1c03a1d0>, state: disappeared, view: <UIView 0x7fce19517b40> not in the window     | <TableViewController 0x7fce1951f7b0>, state: appeared, view: <UIView 0x7fce1c053e60>
(lldb) pvc  <UINavigationController 0x7fce1a03d800>, state: appeared, view: <UILayoutContainerView 0x7fce19500c00>    | <ViewController 0x7fce1c03a1d0>, state: disappeared, view: <UIView 0x7fce19517b40> notin thewindow    | <TableViewController 0x7fce1951f7b0>, state: appeared, view: <UIView 0x7fce1c053e60>
</div>

pclass

循环打印class的继承关系

语法:

Objective-C

Syntax: pclass <object>
Syntax: pclass <object>
</div>

<object> : 要打印继承关系的对象

e.g: 打印一个View对象的继承关系

Objective-C

(lldb) pclass [UIView new]  UIView     | UIResponder     |    | NSObject
(lldb) pclass [UIView new]  UIView    | UIResponder    |    | NSObject
</div>

presponder

打印响应链

语法:

Objective-C

Syntax: presponder <startResponder>
Syntax: presponder <startResponder>
</div>

<startResponder> : UIResponder对象,响应链开始位置

e.g: 打印一个tableView的响应链

Objective-C

(lldb) presponder tableView  <UITableView: 0x7fde54810e00; frame = (0 0; 0 0); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x7fde52519ac0>; layer = <CALayer: 0x7fde5253b4c0>; contentOffset: {0, 0}; contentSize: {0, 220}>     | <UIView: 0x7fde5255c710; frame = (0 0; 600 600); autoresize = W+H; layer = <CALayer: 0x7fde5253b300>>     |    | <TableViewController: 0x7fde52491310>
(lldb) prespondertableView  <UITableView: 0x7fde54810e00; frame = (0 0; 0 0); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x7fde52519ac0>; layer = <CALayer: 0x7fde5253b4c0>;contentOffset: {0, 0};contentSize: {0, 220}>    | <UIView: 0x7fde5255c710; frame = (0 0; 600 600); autoresize = W+H; layer = <CALayer: 0x7fde5253b300>>    |    | <TableViewController: 0x7fde52491310>
</div>

ptv

打印屏幕中显示的tableView,主要是与 pcells 联合使用。如果有多个tableView,打印View层级中最上面的一个

语法:

Objective-C

Syntax: ptv
Syntax: ptv
</div>

e.g: 看看当前最上面是哪个tableView

Objective-C

(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}>
(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}>
</div>

pcells

打印tableView中当前可见的cell,如果有多个tableView,打印View层级中最上面的tableView的可见cell

语法:

Objective-C

Syntax: pcells
Syntax: pcells
</div>

e.g: 看看当前可见的cell有哪些

Objective-C

(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>>  )
(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>>  )
</div>

pinternals

打印一个对象内部的成员变量,这个方法我一般用来看model属性

语法:

Objective-C

Syntax: pinternals <object>
Syntax: pinternals <object>
</div>
  • <object> : 需要打印内部成员变量的对象

e.g: 我们来看看一个 model 内部属性的值

Objective-C

(lldb) pinternals model  (Model) $5 = {    _name = 0x000000010dd1c0a0 @"老鼠爱大米"    _URL = nil    _array = nil    _dictionary = nil    _string = nil    _model = nil  }
(lldb) pinternalsmodel  (Model) $5 = {    _name = 0x000000010dd1c0a0 @"老鼠爱大米"    _URL = nil    _array = nil    _dictionary = nil    _string = nil    _model = nil  }
</div>

pdata

对编码过的NSData进行解码打印,等效于调用 -[NSString initWithData:encoding:]

语法:

Objective-C

Syntax: pdata [--encoding=encoding] <data>
Syntax: pdata [--encoding=encoding] <data>
</div>
  • <data> : 需要打印的data,NSData类型
  • --encoding / -e : 编码类型,如果缺省默认为utf8,主要支持的类型有:

Objective-C

- 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),
- 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),
</div>

e.g: 将一个utf8的NSData打印

Objective-C

(lldb) pdata -e=utf8 data  老鼠爱大米
(lldb) pdata -e=utf8data  老鼠爱大米
</div>

pkp

通过 -valueForKeyPath: 打印 key path 对应的值。

语法:

Objective-C

Syntax: pkp <keypath>
Syntax: pkp <keypath>
</div>
  • <keypath> : 需要打印的路径,如 self.view

说明:以前打印属性一般都用 po obj.xxx ,现在我想用 pkp obj.xxx 是一个更好的选择了,因为 po obj.xxx 是调用getter方法,如果没有getter方法就无法打印了。 pkp obj.xxx 不仅会调用getter方法,没有getter方法还会去查找成员变量

e.g: 打印一下self.view

Objective-C

(lldb) pkp self.view  <UIView: 0x7fd1da52d5d0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fd1da52d740>>
(lldb) pkpself.view  <UIView: 0x7fd1da52d5d0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fd1da52d740>>
</div>

pivar

打印对象成员变量

语法:

Objective-C

Syntax: pivar <object> <ivarName>
Syntax: pivar <object> <ivarName>
</div>
  • <object> : id类型,要打印成员变量的对象
  • <ivarName> : 成员变量的名称,注意:如果是属性,对应成员变量的名字默认有 _ 前缀.

说明:个人觉得这个方法有点鸡肋, pinternals 一下子可以打印出所有的成员变量,用起来更方便,如果你只想打印某一个成员变量,用 pkp 应该更爽

e.g: 继续打印self.view

Objective-C

(lldb) pivar self _view  <UIView: 0x7fd1da52d5d0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fd1da52d740>>
(lldb) pivarself _view  <UIView: 0x7fd1da52d5d0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fd1da52d740>>
</div>

pca

从渲染服务器的角度来打印 layer tree ,命令的完整名字是 PrintCoreAnimationTree ,相当于调用 po [NSString stringWithCString:(char *)CARenderServerGetInfo(0, 2, 0)]

语法:

Objective-C

Syntax: pca
Syntax: pca
</div>

说明:这个命令我也没用过,可能在某些情况下会有用处

e.g: 试一下pca

Objective-C

(lldb) pca  == context 956ecbbf; level 0; pid 28092 [/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/CoreServices/SpringBoard.app/SpringBoard] (secure) at time 34552.989286 ==    (layer [375 667 0] [0 0 375 667] [0.5 0.5 0]    (transform [2 -0 0 0; 0 2 0 0; 0 0 1 0; 0 0 0 1])    (rasterizationScale 2)    (sublayers (array      (layer [187.5 333.5 0] [0 0 375 667] [0.5 0.5 0]        (masksToBounds true)        (sublayers (array          (layer [187.5 333.5 0] [0 0 375 667] [0.5 0.5 0]            (backgroundColor #000000ff)            (sublayers (array              (layer [187.5 333.5 0] [0 0 375 667] [0.5 0.5 0]                (sublayers (array                  (layer [187.5 333.5 0] [0 0 375 667] [0.5 0.5 0]                    (sublayers (array                      (layer [187.5 333.5 0] [0 0 375 667] [0.5 0.5 0]                        (sublayers (array                          (layer [187.5 333.5 0] [0 0 375 667] [0.5 0.5 0]                            (masksToBounds true)                            (edgeAntialiasingMask 0)                            (subclass (layer-host adc504e5                              ....东西太多,省略部分内容.........
(lldb) pca  == context 956ecbbf; level 0; pid 28092 [/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/CoreServices/SpringBoard.app/SpringBoard] (secure) attime 34552.989286 ==     (layer [375 667 0] [0 0 375 667] [0.5 0.5 0]    (transform [2 -0 0 0; 0 2 0 0; 0 0 1 0; 0 0 0 1])    (rasterizationScale 2)    (sublayers (array      (layer [187.5 333.5 0] [0 0 375 667] [0.5 0.5 0]        (masksToBoundstrue)        (sublayers (array          (layer [187.5 333.5 0] [0 0 375 667] [0.5 0.5 0]            (backgroundColor #000000ff)            (sublayers (array              (layer [187.5 333.5 0] [0 0 375 667] [0.5 0.5 0]                (sublayers (array                  (layer [187.5 333.5 0] [0 0 375 667] [0.5 0.5 0]                    (sublayers (array                      (layer [187.5 333.5 0] [0 0 375 667] [0.5 0.5 0]                        (sublayers (array                          (layer [187.5 333.5 0] [0 0 375 667] [0.5 0.5 0]                            (masksToBoundstrue)                            (edgeAntialiasingMask 0)                            (subclass (layer-hostadc504e5                              ....东西太多,省略部分内容.........
</div>

panim

显示是否正在执行动画,相当于调用 p (BOOL)[UIView _isInAnimationBlock]

语法:

Objective-C

Syntax: panim
Syntax: panim
</div>

说明:这个命令也并不常用

e.g: 在动画中,我们打印一下:

Objective-C

(lldb) panim  (BOOL) $0 = YES
(lldb) panim  (BOOL) $0 = YES
</div>

Find

debug的时候,我们经常需要查找一些东西,比如View,viewController等。

fvc

根据viewController的Class名字查找VC

语法:

Objective-C

Syntax: fvc [--name=classNameRegex] [--view=view]
Syntax: fvc [--name=classNameRegex] [--view=view]
</div>
  • --name / -n : string类型参数,根据viewController的Class名字查找viewController
  • --view / -v : UIView类型参数,根据viewController拥有的view查找viewController

说明:上面2个option不能同时使用,只能使用某一个

e.g: 我们先根据名字查找一下VC

Objective-C

(lldb) fvc --name=viewcontroller  0x7fd01a90f310 ViewController
(lldb) fvc --name=viewcontroller  0x7fd01a90f310 ViewController
</div>

e.g: 如果我们知道VC的view地址,也可以根据view来查找VC

Objective-C

(lldb) fvc --view=0x7fd0194194d0  Found the owning view controller.  <ViewController: 0x7fd01a90f310>
(lldb) fvc --view=0x7fd0194194d0  Found theowningviewcontroller.  <ViewController: 0x7fd01a90f310>
</div>

fv

根据view的class名字查找view

语法:

Objective-C

Syntax: fv <classNameRegex>
Syntax: fv <classNameRegex>
</div>
  • <classNameRegex> : view的class名称

e.g: 查找一下屏幕上的UILabel

Objective-C

(lldb) fv uilabel  0x7fd01a91dc10 UILabel
(lldb) fvuilabel  0x7fd01a91dc10 UILabel
</div>

taplog

将点击的view打印出来,这个命令对于查找哪个view非常有帮助

语法:

Objective-C

Syntax: taplog
Syntax: taplog
</div>

说明:要查看的view必须能接收点击事件,也就是他的 userInteractionEnabled 必须为YES才能被找到,UILabel和UIImageView默认 userInteractionEnabled 为NO。

用法:我们需要先将程序暂停,输入 taplog ,程序会自己运行,这时候点击你需要查看的view,控制台上就会显示出你刚刚点击的view相关信息

e.g: 我们先将程序暂停,输入 taplog

Objective-C

(lldb) taplog  Process 28421 resuming
(lldb) taplog  Process 28421 resuming
</div>

程序会自己运行,我们再点击一个UIButton:

Objective-C

<UIButton: 0x7fe6785284e0; frame = (54 244; 46 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7fe678528a50>>
<UIButton: 0x7fe6785284e0; frame = (54 244; 46 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7fe678528a50>>
</div>

flicker

将view闪烁一下,以便于查找view的位置

语法:

Objective-C

Syntax: flicker <viewOrLayer>
Syntax: flicker <viewOrLayer>
</div>
  • <viewOrLayer> 需要闪烁的view或者layer

e.g: 我们来看看self.subView的位置

Objective-C

(lldb) flicker self.subView
(lldb) flickerself.subView
</div>

vs

在view层级中搜索view,并显示出来

语法:

Objective-C

Syntax: vs <view>
Syntax: vs <view>
</div>
  • <view> :要查找的view

说明:相比 fv , vs 主要用于显示view在屏幕上的位置,2个命令可以配合使用

e.g: 假设我们要找屏幕上的一个view

首先用 fv 查找 UIView 类型的view

Objective-C

(lldb) fv uiview  0x7fbcf37228d0 UIView  0x7fbcf3725e90 UIView
(lldb) fvuiview  0x7fbcf37228d0 UIView  0x7fbcf3725e90 UIView
</div>

然后看看这2个view到底哪个是我们想要找的view

Objective-C

(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>>
(lldb) vs 0x7fbcf3725e90     Use thefollowingand (q) toquit.  (w) movetosuperview  (s) movetofirstsubview  (a) movetoprevioussibling  (d) movetonextsibling  (p) printthehierarchy     <UIView: 0x7fbcf3725e90; frame = (0 100; 100 100); layer = <CALayer: 0x7fbcf3712a40>>
</div>

输入命令后他会帮我们在屏幕上用粉红色标志出来 vs 的view

控制台中 vs 的view也有相应log。并且还提示有6种子命令:

  • w : 移动到superview
  • s : 移动到第一个subview
  • a : 移动到前面的同级view
  • d : 移动到后面的同级view
  • p : 打印出层级
  • q : 退出

如果这个不是我们要找的view,可以使用 w , s , a , d , p 命令继续查找

Display

debug的时候,可能有一小半的工作是跟UI打交道,关于UI显示上的东西,也有几个命令

caflush

刷新UI界面。一般我们用LLDB命令改变UI,UI并不会立即更新,我们需要使用 caflush 刷新界面

语法:

Objective-C

Syntax: caflush
Syntax: caflush
</div>

e.g: 我们用命令将label的背景色改为红色

Objective-C

(lldb) fv uilabel  0x7fb3919189d0 UILabel  (lldb) e [((UILabel*)0x7fb3919189d0) setBackgroundColor:[UIColor redColor]]  (lldb) caflush
(lldb) fvuilabel  0x7fb3919189d0 UILabel  (lldb) e [((UILabel*)0x7fb3919189d0) setBackgroundColor:[UIColor redColor]]  (lldb) caflush
</div>

border

给View或者layer加上border

语法:

Objective-C

Syntax: border [--color=color] [--width=width] <viewOrLayer>
Syntax: border [--color=color] [--width=width] <viewOrLayer>
</div>
  • --color / -c : 边框颜色,string类型,比如:’red’, ‘green’ ‘magenta’等,不设置默认为红色
  • --width / -w : 边框宽度,不设置默认为2
  • <viewOrLayer> : 需要设置边框的view或者layer

e.g: 给刚刚的label加上边框

Objective-C

(lldb) fv uilabel  0x7fe713901f10 UILabel  (lldb) border 0x7fe713901f10
(lldb) fvuilabel  0x7fe713901f10 UILabel  (lldb) border 0x7fe713901f10
</div>

unborder

去掉view或者layer的border

语法:

Objective-C

Syntax: unborder <viewOrLayer>
Syntax: unborder <viewOrLayer>
</div>

e.g: 将刚刚加上的border去掉

Objective-C

(lldb) unborder 0x7fe713901f10
(lldb) unborder 0x7fe713901f10
</div>

mask

给view添加一个半透明的矩形mask,用来查看view的位置

语法:

Objective-C

Syntax: mask [--color=color] [--alpha=alpha] <viewOrLayer>
Syntax: mask [--color=color] [--alpha=alpha] <viewOrLayer>
</div>
  • --color / -c : mask的颜色,string类型,比如:’red’, ‘green’,'magenta’等,不设置默认为红色
  • --alpha / -a : mask的透明度,不设置默认为0.5
  • <viewOrLayer> : 需要添加mask的view或者layer

e.g: 假如label是隐藏的,我们给他添加一个mask,看看他的位置在哪儿

Objective-C

(lldb) fv uilabel  0x7fe713901f10 UILabel  (lldb) mask 0x7fe713901f10
(lldb) fvuilabel  0x7fe713901f10 UILabel  (lldb) mask 0x7fe713901f10
</div>

unmask

将添加的mask去掉

语法:

Objective-C

Syntax: unmask <viewOrLayer>
Syntax: unmask <viewOrLayer>
</div>
  • <viewOrLayer> : 需要去掉mask的view或者layer

e.g: 我们将刚刚添加的mask去掉

Objective-C

(lldb) unmask 0x7fe713901f10
(lldb) unmask 0x7fe713901f10
</div>

使用命令之后,我们可以看到什么都没有了,因为label是hidden的

show

显示一个view或者layer,相当于执行 view.hidden = NO

语法:

Objective-C

Syntax: show <viewOrLayer>
Syntax: show <viewOrLayer>
</div>
  • <viewOrLayer> : 需要显示的view或者layer

e.g: 将刚刚的label显示出来

Objective-C

(lldb) show 0x7fe713901f10
(lldb) show 0x7fe713901f10
</div>

hide

隐藏一个view或者layer,相当于执行 view.hidden = YES

语法:

Objective-C

Syntax: hide <viewOrLayer>
Syntax: hide <viewOrLayer>
</div>
  • <viewOrLayer> : 需要隐藏的view或者layer

e.g: 又把label隐藏

Objective-C

(lldb) hide 0x7fe713901f10
(lldb) hide 0x7fe713901f10
</div>

可以看到label位置什么都没有了

slowanim

减慢动画的速度

语法:

Objective-C

Syntax: slowanim <speed>
Syntax: slowanim <speed>
</div>
  • <speed> : 动画的速度,速度越大,动画越快。1表示原始速度。不传参数默认为0.1

e.g: 原始动画我们设置为1s

Objective-C

[UIView animateWithDuration:1 animations:^{      self.subView.frame = frame;  }];
[UIViewanimateWithDuration:1animations:^{      self.subView.frame = frame;  }];
</div>

暂停程序,将动画放慢5倍

Objective-C

(lldb) slowanim 0.2
(lldb) slowanim 0.2
</div>

我们可以看到动画变慢了:

unslowanim

取消 slowanim 效果,将动画速度变为正常

语法:

Objective-C

Syntax: unslowanim
Syntax: unslowanim
</div>

e.g: 我们将刚刚的 slowanim 效果取消

Objective-C

(lldb) unslowanim
(lldb) unslowanim
</div>

Preview

预览功能,帮助我们用命令查看一个view或者图片的真正样子

visualize

用预览App打开UIImage, CGImageRef, UIView, CALayer等对象

语法:

Objective-C

Syntax: visualize <target>
Syntax: visualize <target>
</div>
  • <target> : 需要预览的对象,id类型

e.g: 我们来看看某个image的样子

Objective-C

(lldb) visualize image
(lldb) visualizeimage
</div>

Debug

LLDB主要用于Debug,chisel怎么可能缺少debug相关命令呢?

wivar

为对象的成员变量设置 watchpoint ,更多 watchpoint 相关知识请阅读 <小笨狼与LLDB的故事>

语法:

Objective-C

Syntax: wivar <object> <ivarName>
Syntax: wivar <object> <ivarName>
</div>
  • <object> : 需要为成员变量设置 watchpoint 的对象。id类型
  • <ivarName> : 成员变量的名字,注意一般属性对应的成员变量带有 _ 前缀

e.g: 为self.subView设置watchpoint

Objective-C

(lldb) wivar self _subView  Remember to delete the watchpoint using: watchpoint delete 1
(lldb) wivarself _subView  Remember todeletethewatchpoint using: watchpoint delete 1
</div>

这时候,_subView值改变就会中断程序

bmessage

根据方法名设置断点

语法:

Objective-C

Syntax: bmessage <expression>
Syntax: bmessage <expression>
</div>
  • <expression> : 设置断点的方法名,如: -[MyView setFrame:] , +[MyView awesomeClassMethod] , -[0xabcd1234 setFrame:] 等

说明:一般设置断点,如果这个方法本类没有实现,是父类实现的,断点是无效的。 bmessage 有效避免了这种缺陷,即使本类没有实现,也能设置上断点

e.g: 给self中的 btnAction: 方法设置一个断点

Objective-C

(lldb) bmessage -[self btnAction:]  Setting a breakpoint at -[ViewController btnAction:] with condition (void*)(id)$rdi == 0x00007ff2485311e0  Breakpoint 4: where = TLLDB`-[ViewController btnAction:] at ViewController.m:42, address = 0x000000010c4bb620
(lldb) bmessage -[self btnAction:]  Setting a breakpointat -[ViewController btnAction:]with condition (void*)(id)$rdi == 0x00007ff2485311e0  Breakpoint 4: where = TLLDB`-[ViewController btnAction:]at ViewController.m:42, address = 0x000000010c4bb620
</div>

binside

通过一个相对地址,给framework(library)设置断点

语法:

Objective-C

Syntax: binside <address>
Syntax: binside <address>
</div>
  • <address> : framework的相对地址

pinvocation

打印方法调用堆栈,仅支持x86

语法:

Objective-C

Syntax: pinvocation [--all]
Syntax: pinvocation [--all]
</div>
  • --all / -a : 表示打印所有堆栈,不设置默认只打印当前堆栈

说明:与 bt 命令类似,不过信息比 bt 打印得更详细,遗憾的是只能支持x86

e.g: 打印一下当前堆栈

Objective-C

(lldb) pinvocation  frame #0: 0x000962aa TMasonry`-[ViewController viewDidLoad](self=0x7bf2d9c0, _cmd="viewDidLoad") + 234 at ViewController.m:28  NSInvocation: 0x7bf433e0  self: 0x7bf2d9c0
(lldb) pinvocation  frame #0: 0x000962aa TMasonry`-[ViewController viewDidLoad](self=0x7bf2d9c0, _cmd="viewDidLoad") + 234 at ViewController.m:28  NSInvocation: 0x7bf433e0  self: 0x7bf2d9c0
</div>

打印所有堆栈:

Objective-C

(lldb) pinvocation -a  frame #0: 0x000962aa TMasonry`-[ViewController viewDidLoad](self=0x7bf2d9c0, _cmd="viewDidLoad") + 234 at ViewController.m:28  NSInvocation: 0x7d2bb050  self: 0x7bf2d9c0  ---------------------------------  frame #1: 0x008062ae UIKit`-[UIViewController _sendViewDidLoadWithAppearanceProxyObjectTaggingEnabled] + 44  NSInvocation: 0x7be18a50  self: 0x7bf2d9c0  ---------------------------------  frame #2: 0x0080adce UIKit`-[UIViewController loadViewIfRequired] + 1384  NSInvocation: 0x7bf0cc40  self: 0x7bf2d9c0  ---------------------------------  frame #3: 0x008569f9 UIKit`-[UINavigationController _layoutViewController:] + 52  NSInvocation: 0x7d340c90  self: 0x7c89ee00    Argument:  0xbff69108, address of @} 0x7bf2d9c0  ---------------------------------  frame #4: 0x008572b1 UIKit`-[UINavigationController _updateScrollViewFromViewController:toViewController:] + 421  NSInvocation: 0x7d340cc0  self: 0x7c89ee00    2 Arguments:  0xbff69158, address of @} 0x0  0xbff6915c, address of @} 0x7bf2d9c0  ---------------------------------  frame #5: 0x00857458 UIKit`-[UINavigationController _startTransition:fromViewController:toViewController:] + 145  NSInvocation: 0x7bf24870  self: 0x7c89ee00    3 Arguments:  0xbff69298, address of i} 0  0xbff6929c, address of @} 0x0  0xbff692a0, address of @} 0x7bf2d9c0  ---------------------------------  frame #6: 0x00858854 UIKit`-[UINavigationController _startDeferredTransitionIfNeeded:] + 1038  NSInvocation: 0x7bf16b50  self: 0x7c89ee00    Argument:  0xbff692f8, address of @} 0x0  ---------------------------------  frame #7: 0x00859ada UIKit`-[UINavigationController __viewWillLayoutSubviews] + 68  NSInvocation: 0x7be18930  self: 0x7c89ee00  ---------------------------------  frame #8: 0x00a35c4a UIKit`-[UILayoutContainerView layoutSubviews] + 252  NSInvocation: 0x7bf26ab0  self: 0x7bf40d40  ---------------------------------  frame #9: 0x0070b008 UIKit`-[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 810  NSInvocation: 0x7bf25da0  self: 0x7bf40d40    Argument:  0xbff693b8, address of @} 0x7bf40f80  ---------------------------------  frame #10: 0x01b23059 libobjc.A.dylib`-[NSObject performSelector:withObject:] + 70  NSInvocation: 0x7d2bb6c0  self: 0x7bf40d40    2 Arguments:  0xbff693d8, address of :} layoutSublayersOfLayer:  0xbff693dc, address of @} 0x7bf40f80  ---------------------------------  frame #11: 0x0496b80a QuartzCore`-[CALayer layoutSublayers] + 144  NSInvocation: 0x7bf134c0  self: 0x7bf40f80  ---------------------------------  frame #12: 0x0495f4ee QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 388  ---------------------------------  frame #13: 0x0495f352 QuartzCore`CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 26  ---------------------------------  frame #14: 0x04951e8b QuartzCore`CA::Context::commit_transaction(CA::Transaction*) + 317  ---------------------------------  frame #15: 0x04985e03 QuartzCore`CA::Transaction::commit() + 561  ---------------------------------  frame #16: 0x049866c4 QuartzCore`CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 92  ---------------------------------  frame #17: 0x01f66ffe CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 30  ---------------------------------  frame #18: 0x01f66f5e CoreFoundation`__CFRunLoopDoObservers + 398  ---------------------------------  frame #19: 0x01f5c108 CoreFoundation`CFRunLoopRunSpecific + 504  ---------------------------------  frame #20: 0x01f5befb CoreFoundation`CFRunLoopRunInMode + 123  ---------------------------------  frame #21: 0x00639206 UIKit`-[UIApplication _run] + 540  NSInvocation: 0x7d21fc00  self: 0x7d30bfe0  ---------------------------------  frame #22: 0x0063ebfa UIKit`UIApplicationMain + 160  ---------------------------------  frame #23: 0x00096a0a TMasonry`main(argc=1, argv=0xbff6a898) + 138 at main.m:14  ---------------------------------  frame #24: 0x031caa21 libdyld.dylib`start + 1  ---------------------------------
(lldb) pinvocation -a  frame #0: 0x000962aa TMasonry`-[ViewController viewDidLoad](self=0x7bf2d9c0, _cmd="viewDidLoad") + 234 at ViewController.m:28  NSInvocation: 0x7d2bb050  self: 0x7bf2d9c0  ---------------------------------  frame #1: 0x008062ae UIKit`-[UIViewController _sendViewDidLoadWithAppearanceProxyObjectTaggingEnabled] + 44  NSInvocation: 0x7be18a50  self: 0x7bf2d9c0  ---------------------------------  frame #2: 0x0080adce UIKit`-[UIViewController loadViewIfRequired] + 1384  NSInvocation: 0x7bf0cc40  self: 0x7bf2d9c0  ---------------------------------  frame #3: 0x008569f9 UIKit`-[UINavigationController _layoutViewController:] + 52  NSInvocation: 0x7d340c90  self: 0x7c89ee00     Argument:  0xbff69108, addressof @} 0x7bf2d9c0  ---------------------------------  frame #4: 0x008572b1 UIKit`-[UINavigationController _updateScrollViewFromViewController:toViewController:] + 421  NSInvocation: 0x7d340cc0  self: 0x7c89ee00     2 Arguments:  0xbff69158, addressof @} 0x0  0xbff6915c, addressof @} 0x7bf2d9c0  ---------------------------------  frame #5: 0x00857458 UIKit`-[UINavigationController _startTransition:fromViewController:toViewController:] + 145  NSInvocation: 0x7bf24870  self: 0x7c89ee00     3 Arguments:  0xbff69298, addressof i} 0  0xbff6929c, addressof @} 0x0  0xbff692a0, addressof @} 0x7bf2d9c0  ---------------------------------  frame #6: 0x00858854 UIKit`-[UINavigationController _startDeferredTransitionIfNeeded:] + 1038  NSInvocation: 0x7bf16b50  self: 0x7c89ee00     Argument:  0xbff692f8, addressof @} 0x0  ---------------------------------  frame #7: 0x00859ada UIKit`-[UINavigationController __viewWillLayoutSubviews] + 68  NSInvocation: 0x7be18930  self: 0x7c89ee00  ---------------------------------  frame #8: 0x00a35c4a UIKit`-[UILayoutContainerView layoutSubviews] + 252  NSInvocation: 0x7bf26ab0  self: 0x7bf40d40  ---------------------------------  frame #9: 0x0070b008 UIKit`-[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 810  NSInvocation: 0x7bf25da0  self: 0x7bf40d40     Argument:  0xbff693b8, addressof @} 0x7bf40f80  ---------------------------------  frame #10: 0x01b23059 libobjc.A.dylib`-[NSObject performSelector:withObject:] + 70  NSInvocation: 0x7d2bb6c0  self: 0x7bf40d40     2 Arguments:  0xbff693d8, address of :} layoutSublayersOfLayer:  0xbff693dc, addressof @} 0x7bf40f80  ---------------------------------  frame #11: 0x0496b80a QuartzCore`-[CALayer layoutSublayers] + 144  NSInvocation: 0x7bf134c0  self: 0x7bf40f80  ---------------------------------  frame #12: 0x0495f4ee QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 388  ---------------------------------  frame #13: 0x0495f352 QuartzCore`CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 26  ---------------------------------  frame #14: 0x04951e8b QuartzCore`CA::Context::commit_transaction(CA::Transaction*) + 317  ---------------------------------  frame #15: 0x04985e03 QuartzCore`CA::Transaction::commit() + 561  ---------------------------------  frame #16: 0x049866c4 QuartzCore`CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 92  ---------------------------------  frame #17: 0x01f66ffe CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 30  ---------------------------------  frame #18: 0x01f66f5e CoreFoundation`__CFRunLoopDoObservers + 398  ---------------------------------  frame #19: 0x01f5c108 CoreFoundation`CFRunLoopRunSpecific + 504  ---------------------------------  frame #20: 0x01f5befb CoreFoundation`CFRunLoopRunInMode + 123  ---------------------------------  frame #21: 0x00639206 UIKit`-[UIApplication _run] + 540  NSInvocation: 0x7d21fc00  self: 0x7d30bfe0  ---------------------------------  frame #22: 0x0063ebfa UIKit`UIApplicationMain + 160  ---------------------------------  frame #23: 0x00096a0a TMasonry`main(argc=1, argv=0xbff6a898) + 138 at main.m:14  ---------------------------------  frame #24: 0x031caa21 libdyld.dylib`start + 1  ---------------------------------
</div>

Accessibility

这个模块的命令主要利用了 UIAccessibility 相关特性,需要开启Accessibility功能才能使用, UIAccessibility 详细资料可以阅读 UIAccessibility

fa11y

根据view的 accessibilityLabel 查找view

语法:

Objective-C

Syntax: fa11y <labelRegex>
Syntax: fa11y <labelRegex>
</div>
  • <labelRegex> : 需要匹配的text

说明:UILabel,UIButton的 accessibilityLabel 等于title

e.g: 我们根据显示的文案,查找相应的控件

Objective-C

(lldb) fa11y 妈妈  (UILabel 0x176b5bd0) 妈妈叫你回家吃饭了
(lldb) fa11y 妈妈  (UILabel 0x176b5bd0) 妈妈叫你回家吃饭了
</div>

pa11y

打印view层级中所有的 accessibilityLabel

语法:

Objective-C

Syntax: pa11y <aView>
Syntax: pa11y <aView>
</div>
  • <aView> : 需要打印层级的View,UIView类型

e.g: 我们打印一下self.view层级中所有的 accessibilityLabel

Objective-C

(lldb) pa11y self.view  UIView self.view     | (UIButton 0x176b4600) Button     | (UILabel 0x176b5bd0) 妈妈叫你回家吃饭了
(lldb) pa11yself.view  UIView self.view    | (UIButton 0x176b4600) Button    | (UILabel 0x176b5bd0) 妈妈叫你回家吃饭了
</div>

End

chisel的命令就介绍到这里,东西挺多的,没必要全部都记住,我把这个写出来就打算当做一个文档,以后记不起哪个命令就查一下自己的博客。

不过chisel确实是个好东西,工欲善其事,必先利其器。要想要方便的调bug,chisel绝对值得你拥有

参考

</div>

来自: http://ios.jobbole.com/83589/