iOS调试技巧之LLDB,调试技术

明日给大家介绍的始末,无关联任何功能性开发技术,但又对开发的频率影响至深,那就是调剂技术。

图片 1

前言

明日花了一天的时日毕竟把iOS的二种常见的调试方法给上学了一晃,在此处给大家享受一下LLDB的应用,同时也是为了忘记以后,方便寻找。

何为调试呢,比如大家用 print
函数在指定地方展开输出,来定位有个别节点的变量内的取值:

归纳图解

LLDB介绍

LLDB 是3个负有 REPL
的天性和 C++ ,Python
插件的开源调试器。LLDB
绑定在 Xcode
内部,存在于主窗口底部的控制莱比锡。调试器允许你在程序运营的一定时暂停它,你可以查阅变量的值,执行自定的通令,并且依据你所认为非常的步子来操作程序的开展。

let result = parseJSON("[1,2,3]");
print(result);

result = parseJSON("error");
print(result);

在 LLDB 命令行中,对于每种流程控制按钮都有照应的通令。

  • n 命令, 代表 Step Over 操作。
  • s 命令, 代表 Step Into 操作。
  • finish命令,代表 Step Out 操作。
  • c命令, 代表苏醒程序执行操作。
基础

此间有二个容易易行的小程序,它会打印三个字符串。注意断点已经被加在第 8
行。断点可以经过点击 Xcode 的源码窗口的侧面槽举办创办。

图片 2

Image_2014-11-20_at_10.01.46_PM.png

先后会在这一行为止运营,并且决定台会被打开,允许大家和调试器交互。那大家相应打些什么吗?
help
最简便易行命令是 help
,它会罗列出富有的授命。若是您忘掉了一个下令是做什么样的,大概想知道越多的话,你可以经过
help <command>
来打听愈来愈多细节,例如 help print
依旧 help thread假使你依然忘记了 help指令是做什么的,你可以试试help
help然而你一旦知道这么做,那就印证您几乎还没忘光这一个命令。
print
打印值很简单;只要试试 print
命令:

图片 3

Image_2014-11-20_at_10.09.38_PM.png

LLDB 实际上会作前缀匹配。所以您也可以行使 prin
,pri,恐怕 p。但你无法采纳 pr,因为 LLDB 无法免去和 process的歧义
(幸运的是 p并从未歧义)。
你或许还留意到了,结果中有个 $0。实际上你可以使用它来指向这几个结果。试试
print $0 + 7,你会看出 106。任何以卢比符开始的东西都以存在于 LLDB
的命名空间的,它们是为着援救你举办调剂而留存的。
expression
一经想改变3个值如何是好?你大概会猜 modify。其实那时候我们要用到的是
expression那一个有利的授命。

图片 4

那不仅仅会转移调试器中的值,实际上它改变了程序中的值。那时候继续执行程序,将会打印
42 red balloons。神奇呢。
注意,从未来上马,大家将会偷懒分别以 p和 e来代替 print和
expression。
什么是 print 命令
考虑一个妙趣横生的表明式:p count = 18。若是大家运营那条命令,然后打印
count的故事情节。大家将看到它的结果与expression count = 18同一。和
expression不一样的是,print命令不必要参数。比如 e -h
+17中,你很难区分到底是以 -h为标识,仅仅执行 +17呢,照旧要计算 17和
h的差值。连字符号确实很令人质疑,你大概得不到温馨想要的结果。
幸运的是,化解方案很粗略。用
–来表征标识的终结,以及输入的启幕。假诺想要 -h作为标识,就用 e -h —
+17,假如想计算它们的差值,就接纳 e — -h
+17。因为一般的话不使用标识的动静相比较多,所以 e
–就有了2个简写的格局,那就是print。
输入 help print,然后向下滚动,你会发觉:

'print' is an abbreviation for 'expression --'.
(print是 `expression --` 的缩写)

打印对象
尝试输入

p objects

输出会有点啰嗦

(NSString *) $7 = 0x0000000104da4040 @"red balloons"

万一大家品尝打印结构更复杂的对象,结果依然会更糟

(lldb) p @[ @"foo", @"bar" ]
(NSArray *) $8 = 0x00007fdb9b71b3e0 @"2 objects" 

实质上,大家想看的是目的的 description
方法的结果。作者么须求利用 -O(字母 O,而不是数字 0) 标志告诉
expression命令以 对象(Object) 的艺术来打印结果。

(lldb) e -O -- $8
<__NSArrayI 0x7fdb9b71b3e0>(
  foo,
  bar
)

侥幸的是,e -o –有也某个名,那就是 po
(print object 的缩写),我们得以应用它来开展简化:

(lldb) po $8
<__NSArrayI 0x7fdb9b71b3e0>(
foo,
bar)
(lldb) po @"lunar"
lunar
(lldb) p @"lunar"
(NSString *) $13 = 0x00007fdb9d0003b0 @"lunar"

打印变量
能够给 print指定不相同的打印格式。它们都以以 print/<fmt>或然简化的
p/<fmt>格式书写。上面是一对例子:
暗许的格式

(lldb) p 1616

十六进制:

(lldb) p/x 16
0x10

二进制 (t代表 two):

(lldb) p/t 16
0b00000000000000000000000000010000
(lldb) p/t (char)16
0b00010000

您也得以动用 p/c打印字符,或许 p/s打印以空终止的字符串 (译者注:以 ‘\0’
结尾的字符串)。
这里是格式的一体化清单。
变量
今昔您曾经可以打印对象和总结类型,并且了解什么采用 expression
一声令下在调试器中修改它们了。将来让我们使用部分变量来裁减输入量。似乎您可以在
C 语言中用 int a = 0来声称一个变量一样,你也可以在 LLDB
中做相同的事体。然则为了能动用评释的变量,变量必须以日币符初阶。

(lldb) e int $a = 2
(lldb) p $a * 19
38
(lldb) e NSArray *$array = @[ @"Saturday", @"Sunday", @"Monday" ]
(lldb) p [$array count]
2
(lldb) po [[$array objectAtIndex:0] uppercaseString]
SATURDAY
(lldb) p [[$array objectAtIndex:$a] characterAtIndex:0]
error: no known method '-characterAtIndex:'; cast the message send to the method's return type
error: 1 errors parsing expression

正剧了,LLDB 不可以鲜明涉及的类型
(译者注:再次来到的种类)。这种业务平时暴发,给个验证就好了:

(lldb) p (char)[[$array objectAtIndex:$a] characterAtIndex:0]
'M'
(lldb) p/d (char)[[$array objectAtIndex:$a] characterAtIndex:0]
77

变量使调试器变的不难选取得多,想不到呢?
流程控制
当您通过 Xcode 的源码编辑器的侧面槽 (大概通过下边的不二法门)
插入2个断点,程序到达断点时会就会截至运转。
调节条上会现身多个你能够用来决定程序的推行流程的按钮。

图片 5

从左到右,七个按钮分别是:continue,step over,step into,step out。
首先个,continue 按钮,会撤消程序的中止,允许程序不奇怪化执行
(要么直接执行下去,要么到达下1个断点)。在 LLDB 中,你可以运用 process
continue命令来达到同等的效用,它的别名为 continue
,或然也足以缩写为 c。
其次个,step over
按钮,会以黑盒的方法进行一行代码。如若所在那行代码是一个函数调用,那么就不会跳进这一个函数,而是会举行这么些函数,然后继续。LLDB
则足以利用 thread step-over,next,恐怕 n命令。
比方你真正想跳进2个函数调用来调节只怕检查程序的施行意况,那就用第多个按钮,step
in,只怕在LLDB中使用 thread step in,step,恐怕s命令。注意,当前行不是函数调用时,next和 step
意义是一致的。
绝半数以上人了然 c,n 和 s,然而实际上还有第四个按钮,step
out。借使您早就不小心跳进1个函数,但实际上你想跳过它,常见的影响是重新的运营n直到函数再次回到。其实那种气象,step out
按钮是您的基督。它会继续执行到下二个回到语句 (直到三个堆栈帧截止)
然后再次停下。
例子
设想上边一段程序:

图片 6

要是我们运营程序,让它甘休在断点,然后实施下边一些列命令:

p i
n
s
p i
finish
p i
frame info

这边,frame info会告诉你日前的行数和源码文件,以及其余一些音讯;查看
help frame,help thread和 help
process来取得愈来愈多消息。这一串命令的结果会是哪些?看答案在此之前请先想一想。

(lldb) p i
(int) $0 = 99
(lldb) n
2014-11-22 10:49:26.445 DebuggerDance[60182:4832768] 101 is odd!
(lldb) s
(lldb) p i
(int) $2 = 110
(lldb) finish
2014-11-22 10:49:35.978 DebuggerDance[60182:4832768] 110 is even!
(lldb) p i
(int) $4 = 99
(lldb) frame info
frame #0: 0x000000010a53bcd4 DebuggerDance`main + 68 at main.m:17

它从来在 17 行的缘由是 finish命令一直运维到 is伊芙n() 函数的
return,然后登时停下。注意尽管它还在 17 行,其实那行已经被执行过了。
Thread Return
调剂时,还有三个很棒的函数可以用来支配程序流程:thread
return。它有一个可选参数,在实施时它会把可选参数加载进重回寄存器里,然后随即实施回来命令,跳出当前栈帧。那象征那函数剩余的有些不会被实施。那会给
ARAV4C
的引用计数造成一些难题,可能会使函数内的清理部分失效。然则在函数的起初执行那一个命令,是个十二分好的隔断那些函数,伪造重返值的章程

让我们有点修改一下地点代码段并运维:

p i
s
thread return NO
n
p even0
frame info

看答案前考虑一下。下边是答案:

(lldb) p i
(int) $0 = 99
(lldb) s
(lldb) thread return NO
(lldb) n
(lldb) p even0
(BOOL) $2 = NO
(lldb) frame info
frame #0: 0x00000001009a5cc4 DebuggerDance`main + 52 at main.m:17

断点
大家都把断点作为二个甘休程序运转,检查当前情状,追踪 bug
的法门。不过只要大家改变和断点交互的措施,很多政工都改为可能。
断点允许控制程序如哪天候截止,然后允许命令的周转。

想象把断点放在函数的开始,然后用 thread
return命令重写函数的行事,然后继续。想象一下让那个进程自动化,听起来不错,不是吧?
管理断点
Xcode 提供了一一日千里工具来创制和治本断点。大家会一个个看復苏并介绍 LLDB
中等价的命令 (是的,你可以在调试器内部添加断点)。

在 Xcode
的左侧面板,有一组按钮。其中1个看起来像断点。点击它打开断点导航,那是2个可以便捷管理全体断点的面板。

图片 7

Image_2014-11-22_at_11.38.24_AM.png

在此间您可以见见有着的断点 – 在 LLDB 中通过 breakpoint list
(可能 br li) 命令也做同样的事务。你也足以点击单个断点来开启或关闭 – 在
LLDB 中应用 breakpoint enable <breakpointID>
和 breakpoint disable <breakpointID>:

(lldb) br li
Current breakpoints:
1: file = '/Users/arig/Desktop/DebuggerDance/DebuggerDance/main.m', line = 16, locations = 1, resolved = 1, hit count = 1

    1.1: where = DebuggerDance`main + 27 at main.m:16, address = 0x000000010a3f6cab, resolved, hit count = 1

(lldb) br dis 1
1 breakpoints disabled.
(lldb) br li
Current breakpoints:
1:file='/Users/arig/Desktop/DebuggerDance/DebuggerDance/main.m', line = 16, locations = 1 Options: disabled 

    1.1: where = DebuggerDance`main + 27 at main.m:16, address = 0x000000010a3f6cab, unresolved, hit count = 1

(lldb) br del 1
1 breakpoints deleted; 0 breakpoint locations disabled.
(lldb) br li
No breakpoints currently set.

创制断点
在上面的事例中,大家通过在源码页面器的滚槽
16上点击来创制断点。你可以经过把断点拖拽出滚槽,然后释放鼠标来删除断点
(消失时会有三个万分迷人的噗的弹指间的动画片)。你也足以在断点导航页采用断点,然后按下删除键删除。
要在调试器中开创断点,可以应用 breakpoint set命令。

(lldb) breakpoint set -f main.m -l 16
Breakpoint 1: where = DebuggerDance`main + 27 at main.m:16, address = 0x000000010a3f6cab

也可以采取缩写方式 br。固然 b是多个通通两样的命令
(_regexp-break的缩写),但恰恰也落实和地点一样的出力。

(lldb) b main.m:17
Breakpoint 2: where = DebuggerDance`main + 52 at main.m:17, address = 0x000000010a3f6cc4

也足以在一个标志 (C 语言函数) 上创制断点,而完全不用指定哪一行

(lldb) b isEven
Breakpoint 3: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x000000010a3f6d00
(lldb) br s -F isEven
Breakpoint 4: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x000000010a3f6d00

这一个断点会准确的告一段落在函数的早先。Objective-C 的章程也完全可以:

(lldb) breakpoint set -F "-[NSArray objectAtIndex:]"
Breakpoint 5: where = CoreFoundation`-[NSArray objectAtIndex:], address = 0x000000010ac7a950
(lldb) b -[NSArray objectAtIndex:]
Breakpoint 6: where = CoreFoundation`-[NSArray objectAtIndex:], address = 0x000000010ac7a950
(lldb) breakpoint set -F "+[NSSet setWithObject:]"
Breakpoint 7: where = CoreFoundation`+[NSSet setWithObject:], address = 0x000000010abd3820
(lldb) b +[NSSet setWithObject:]
Breakpoint 8: where = CoreFoundation`+[NSSet setWithObject:], address = 0x000000010abd3820

只要想在 Xcode 的UI上开创符号断点,你可以点击断点栏左侧的 +
按钮。

图片 8

Image_2014-11-22_at_11.52.50_AM.png

接下来拔取第多少个接纳:

图片 9

Image_2014-11-22_at_11.54.44_AM.png

这时会并发3个弹出框,你可以在里头添加例如 -[NSArray
objectAtIndex:]如此那般的符号断点。那样每次调用那些函数的时候,程序都会停下,不管是您调用如故苹果调用。
如果你 Xcode 的 UI 上右击任意断点,然后选取 “Edit Breakpoint”
的话,会有一些可怜诱人的挑三拣四。

图片 10

Image_2014-11-22_at_11.58.06_AM.png

此地,断点已经被修改为只有当 i是 99 的时候才会告一段落。你也得以使用
“ignore” 选项来告诉断点最初的 n次调用 (并且条件为实在时候)
的时候不要为止。
接下去介绍 ‘Add Action’ 按钮…
断点行为 (Action)
上边的例子中,你大概想领悟每三次到达断点的时候 i
的值。我们得以应用 p
i作为断点行为。这样每便到达断点的时候,都会活动运维这几个命令。

图片 11

Screen_Shot_2014-11-22_at_12.01.32_PM.png

你也足以添加五个表现,能够是调试器命令,shell
命令,也得以是更直接的打印:

图片 12

可以看到它打印 i,然后大声念出相当句子,接着打印了自定义的表明式。

下边是在 LLDB 而不是 Xcode 的 UI 中做这个的时候,看起来的规范。

(lldb) breakpoint set -F isEven
Breakpoint 1: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x00000001083b5d00
(lldb) breakpoint modify -c 'i == 99' 1
(lldb) breakpoint command add 1
Enter your debugger command(s). Type 'DONE' to end.
> p i
> DONE
(lldb) br li 1
1: name = 'isEven', locations = 1, resolved = 1, hit count = 0
     Breakpoint commands: 
        p i
Condition: i == 99 

    1.1: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x00000001083b5d00, resolved, hit count = 0 

收受来说说自动化。
赋值后持续运营
看编辑断点弹出窗口的底部,你还汇合到一个项: “Automatically continue
after evaluation actions.”

。它只是是二个取舍框,可是却很有力。选中它,调试器会运作你抱有的指令,然后继续运转。看起来似乎没有举办其它断点一样
(除非断点太多,运营需求一段时间,拖慢了你的次序)。

其一选项框的法力和让最后断点的终极二个表现是 continue
无异于。选框只是让这么些操作变得更简短。调试器的出口是:

(lldb) breakpoint set -F isEven
Breakpoint 1: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x00000001083b5d00
(lldb) breakpoint command add 1
Enter your debugger command(s). Type 'DONE' to end.
> continue
> DONE
(lldb) br li 1
1: name = 'isEven', locations = 1, resolved = 1, hit count = 0   
      Breakpoint commands: 
          continue 
    1.1: where = DebuggerDance`isEven + 16 at main.m:4, address = 0x00000001083b5d00, resolved, hit count = 0

执行断点后自动三番五次运行,允许你一点一滴通过断点来修改程序!你可以在某一行终止,运行一个expression命令来改变变量,然后继续运转。
例子
合计所谓的”打印调试”技术吗,不要这么做:

NSLog(@"%@", whatIsInsideThisThing);

而是用个打印变量的断点替换 log 语句,然后继续运维。
也不要:

int calculateTheTrickyValue { 
    return 9; 
    /* 
    Figure this out later. 
    ...
}

而是加1个行使 thread return 9命令的断点,然后让它继续运转。

标志断点加上 action 真的很有力。你也足以在您爱人的 Xcode
工程上添加一些断点,并且拉长大声诵读有些事物的
action。看看她们要花多久才能弄通晓爆发了什么样。
全盘在调试器内运营
在上马舞蹈在此之前,还有一件事要看一看。实际上你可以在调试器中实行别的C/Objective-C/C++/斯维夫特 的命令。唯一的后天不足就是无法创设新函数…
那意味不可以创制新的类,block,函数,有虚构函数的 C++
类等等。除此之外,它都可以做。

大家得以报名分配一些字节:

(lldb) e char *$str = (char *)malloc(8)
(lldb) e (void)strcpy($str, "munkeys")
(lldb) e $str[1] = 'o'
(char) $0 = 'o'
(lldb) p $str
(char *) $str = 0x00007fd04a900040 "monkeys"

咱俩得以查阅内存 (使用 x命令),来探望新数组中的七个字节:

(lldb) x/4c $str
0x7fd04a900040: monk

大家也得以去掉 3 个字节
(x命令须求斜引号,因为它只有贰个内存地址的参数,而不是表达式;使用 help
x来得到更加多音信):

(lldb) x/1w `$str + 3`
0x7fd04a900043: keys

做完领会后,一定毫无忘了释放内存,那样才不会内存败露。(哈,即便这是调试器用到的内存):

(lldb) e (void)free($str)

让大家跳舞
如今大家曾经知晓基本的步调了,是时候开首跳舞并玩一些癫狂的工作了。小编早已写过一篇
NSArray
纵深探索
的博客。那篇博客用了无数
NSLog
话语,但实在本身的全体探索都以在调试器中成就的。看看您能无法弄精通如何是好的,那会是多少个妙不可言的练习。
不用断点调试
程序运维时,Xcode 的调剂条上会出现抛锚按钮,而不是持续按钮:

图片 13

点击按钮会暂停 app (那会运作 process interrupt命令,因为 LLDB
总是在背后运转)。那会让你可以访问调试器,但看起来可以做的业务不多,因为在当前功能域没有变量,也平昔不一定的代码让您看。

这就是有趣的地方。假若您正在周转 iOS app,你可以尝试那一个:
(因为全局变量是可访问的)

(lldb) po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]
<UIWindow: 0x7f82b1fa8140; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x7f82b1fa92d0>; layer = <UIWindowLayer: 0x7f82b1fa8400>>
   | <UIView: 0x7f82b1d01fd0; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x7f82b1e2e0a0>>

你可以看看全体层次。Chisel
中 pviews就是这么落成的。
更新UI
有了地点的出口,我们得以取得这么些 view:

(lldb) e id $myView = (id)0x7f82b1d01fd0

下一场在调试器中改变它的背景象:

(lldb) e (void)[$myView setBackgroundColor:[UIColor blueColor]]

只是唯有程序继续运行之后才会看到界面的更动。因为改变的内容必须被发送到渲染服务中,然后突显才会被更新。

渲染服务实在是贰个其它的进程 (被称作 backboardd
)。那就是说即使大家正在调试的情节所在的历程被打断了,backboardd也如故持续运维着的。
那象征你可以运维上面的命令,而不用持续运维程序:

(lldb) e (void)[CATransaction flush]

哪怕你依旧在调试器中,UI
也会在模拟器或许真机上实时更新。Chisel
为此提供了三个别名叫做 caflush,那么些命令被用来兑现任何的飞快命令,例如
hide <view>,show <view>以及其余过多发令。全体
Chisel
的下令都有文档,所以安装后随便运行 help show来看更多新闻。
Push 一个 View Controller
设想3个以 UINavigationController为 root ViewController
的施用。你可以通过上边的吩咐,轻松地赢得它:

(lldb) e id $nvc = [[[UIApplication sharedApplication] keyWindow] rootViewController]

然后 push 一个 child view controller:

(lldb) e id $vc = [UIViewController new]
(lldb) e (void)[[$vc view] setBackgroundColor:[UIColor yellowColor]]
(lldb) e (void)[$vc setTitle:@"Yay!"]
(lldb) e (void)[$nvc pushViewContoller:$vc animated:YES]

末尾运营上边的下令:

(lldb) caflush // e (void)[CATransaction flush]

navigation Controller 就会立即就被 push 到你面前。
招来按钮的 target
想像你在调试器中有三个 $myButton
的变量,可以是创办出来的,也可以是从 UI
上抓取出来的,可能是你打住在断点时的3个部分变量。你想知道,按钮按下的时候什么人会吸收到按钮发出的
action。非常简单:

(lldb) po [$myButton allTargets]
{( 
    <MagicEventListener: 0x7fb58bd2e240>
)}
(lldb) po [$myButton actionsForTarget:(id)0x7fb58bd2e240 forControlEvent:0]
<__NSArrayM 0x7fb58bd2aa40>(_
handleTap:
)

未来您或者想在它暴发的时候加3个断点。在 -[MagicEventListener
_handleTap:]设置二个标记断点就足以了,在 Xcode 和 LLDB
中都可以,然后你就能够点击按钮并停在您所企望的地方了。
着眼实例变量的转移
假定你有3个 UIView,不掌握为啥它的 _layer
实例变量被重写了
(不好)。因为有可能并不涉及到艺术,大家无法接纳标志断点。相反的,大家想监视怎么时候这么些地方被写入。

先是,我们需求找到 _layer那个变量在目标上的争辩地点:

(lldb) p (ptrdiff_t)ivar_getOffset((struct Ivar *)class_getInstanceVariable([MyView class], "_layer"))
(ptrdiff_t) $0 = 8

后天我们清楚 ($myView + 8)
是被写入的内存地址:

(lldb) watchpoint set expression -- (int *)$myView + 8
Watchpoint created: Watchpoint 3: addr = 0x7fa554231340 size = 8 state = enabled type = w 
    new value: 0x0000000000000000

这被以 wivar $myView _layer加入到
Chisel
中。
非重写方法的号子断点
倘若你想理解 -[MyViewController
viewDidAppear:]怎样时候被调用。假设那几个法子并不曾在MyViewController中贯彻,而是在其父类中落到实处的,该咋做吧?试着设置3个断点,会并发以下结果:

(lldb) b -[MyViewController viewDidAppear:]
Breakpoint 1: no locations (pending).
WARNING: Unable to resolve breakpoint to any actual locations.

因为 LLDB
会查找一个符号,不过其实在那些类上却找不到,所以断点也永远不会接触。你须要做的是为断点设置三个尺度
[self isKindOfClass:[MyViewController class]],然后把断点放在
UIViewController上。没有难点处境下那样设置多少个规范得以健康办事。不过此地不会,因为大家从未父类的兑现。

viewDidAppear:是苹果已毕的办法,由此未曾它的标志;在格局内尚未
self。即便想在符号断点上利用 self,你不能够不理解它在哪里(它只怕在寄存器上,也大概在栈上;在 x86 上,你可以在 $esp+4
找到它)。不过那是很痛心的,因为前几天您不或然不至少知道八种体系布局
(x86,x86-64,armv7,armv64)。想象你要求花多少日子去读书命令集以及它们每1个的调用约定,然后正确的写二个在您的超类上设置断点并且条件不利的吩咐。幸运的是,这几个在
Chisel
被消除了。那被改为 bmessage:

(lldb) bmessage -[MyViewController viewDidAppear:]
Setting a breakpoint at -[UIViewController viewDidAppear:] with condition (void*)object_getClass((id)$rdi) == 0x000000010e2f4d28
Breakpoint 1: where = UIKit`-[UIViewController viewDidAppear:], address = 0x000000010e11533c

LLDB 和 Python
LLDB 有内建的,完整的
Python
资助。在LLDB中输入 script,会打开3个 Python REPL。你也足以输入一行
python 语句作为 script 命令的参数,那足以运维 python 语句而不进入REPL:

(lldb) script import os
(lldb) script os.system("open http://www.objc.io/")

这么就同意你创立各类酷的下令。把下边的言辞放到文件 ~/myCommands.py中:

def caflushCommand(debugger, command, result, internal_dict): 
    debugger.HandleCommand("e (void)[CATransaction flush]")

然后再 LLDB 中运行:

command script import ~/myCommands.py

恐怕把那行命令放在 /.lldbinit里,那样每一趟进入 LLDB
时都会自行运营。Chisel
其实就是三个 Python 脚本的汇聚,这一个脚本拼接 (命令) 字符串 ,然后让 LLDB
执行。很粗略,不是吗?

相信我们我们看来类似那样的代码都不会目生,猜测为开发者朋友都会或多或少的用如此的法门对程序举行调节。

Po 打印变量

 Po 变量
LLDB调试命令初探(copy)

常用命令:

  • expr
    可以在调节时动态执行指定表达式,并将结果打印出来。常用于在调节进度中修改变量的值。

    图片 14

    图四:expr截图

    如图设置断点,然后运行程序。程序中断后输入下边的一声令下:

expr a=2

你会看出如下的输出:

(int) $0 = 2

此起彼伏运维程序,程序输出的音信是:

实际值:2

很显然能够观察,变量a的值被更改。
除此之外,还足以应用那几个命令新声飞鹤个变量对象,如:

expr int $b=2
p $b

上面的吩咐用于出口新注解对象的值。(注意,对象名前要加$)

  • call

call即是调用的趣味。其实上述的po和p也有调用的效应。因而一般只在不必要展示输出,或是艺术无重返值时采取call。
和地点的一声令下一样,大家还是在viewDidLoad:里面安装断点,然后在先后中断的时候输入上面的命令:

call [self.view setBackgroundColor:[UIColor redColor]]

继承运营程序,看看view的背景颜色是还是不是成为水绿的了!在调试的时候灵活运用call命令可以起到一石多鸟的功用。

  • bt

打印调用堆栈,加all可打印全部thread的堆栈。不详细举例表达,感兴趣的情人能够团结尝试。

  • image

image
命令可用于寻址,有七个结合命令。比较实用的用法是用以寻找栈地址对应的代码地点。
上边作者写了一段代码

NSArray *arr=[[NSArray alloc] initWithObjects:@"1",@"2", nil];
NSLog(@"%@",arr[2]);

这段代码有不言而喻的错误,程序运转那段代码后会抛出上面的老大:

*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 2 beyond bounds [0 .. 1]'
*** First throw call stack:
( 
  0 CoreFoundation 0x0000000101951495 __exceptionPreprocess + 165 
  1 libobjc.A.dylib 0x00000001016b099e objc_exception_throw + 43 
  2 CoreFoundation 0x0000000101909e3f -[__NSArrayI objectAtIndex:] + 175 
  3 ControlStyleDemo 0x0000000100004af8 -[RootViewController viewDidLoad] + 312 
  4 UIKit 0x000000010035359e -[UIViewController loadViewIfRequired] + 562 
  5 UIKit 0x0000000100353777 -[UIViewController view] + 29 
  6 UIKit 0x000000010029396b -[UIWindow addRootViewControllerViewIfPossible] + 58 
  7 UIKit 0x0000000100293c70 -[UIWindow _setHidden:forced:] + 282 
  8 UIKit 0x000000010029cffa -[UIWindow makeKeyAndVisible] + 51 
  9 ControlStyleDemo 0x00000001000045e0 -[AppDelegate application:didFinishLaunchingWithOptions:] + 672 
  10 UIKit 0x00000001002583d9 -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 264 
  11 UIKit 0x0000000100258be1 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1605 
  12 UIKit 0x000000010025ca0c -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 660 
  13 UIKit 0x000000010026dd4c -[UIApplication handleEvent:withNewEvent:] + 3189 
  14 UIKit 0x000000010026e216 -[UIApplication sendEvent:] + 79 
  15 UIKit 0x000000010025e086 _UIApplicationHandleEvent + 578 
  16 GraphicsServices 0x0000000103aca71a _PurpleEventCallback + 762  
  17 GraphicsServices 0x0000000103aca1e1 PurpleEventCallback + 35 
  18 CoreFoundation 0x00000001018d3679 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 41
  19 CoreFoundation 0x00000001018d344e __CFRunLoopDoSource1 + 478
  20 CoreFoundation 0x00000001018fc903 __CFRunLoopRun + 1939 
  21 CoreFoundation 0x00000001018fbd83 CFRunLoopRunSpecific + 467 
  22 UIKit 0x000000010025c2e1 -[UIApplication _run] + 609 
  23 UIKit 0x000000010025de33 UIApplicationMain + 1010 
  24 ControlStyleDemo 0x0000000100006b73 main + 115     
  25 libdyld.dylib 0x0000000101fe95fd start + 1 
  26 ??? 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

近来,我们狐疑出错的地址是0x0000000一千04af8(可以依照实施文书名判断,可能最小的栈地址)。为了进一步精明确位,大家可以输入以下的下令:

image lookup --address 0x0000000100004af8

命令执行后再次来到:

Address: ControlStyleDemo[0x0000000100004af8] (ControlStyleDemo.__TEXT.__text + 13288)
Summary: ControlStyleDemo`-[RootViewController viewDidLoad] + 312 at RootViewController.m:53

我们可以看来,出错的职责是RootViewController.m的第肆3行。

愈来愈多的下令可以参见以此网址。其它,facebook开源了她们扩张的LLDB命令库,有趣味的爱侣也足以设置看看。

普遍难点
下边大家简要的就学了什么行使LLDB指令。但有时我们在行使那些LLDB指令的时候,仍然或然会遇上一些难点。
暧昧类型或然项目不合营
例如下边这么些命令。

(lldb) p NSLog(@"%@",[self.view viewWithTag:1001])
error: 'NSLog' has unknown return type; cast the call to its declared return type
error: 1 errors parsing expression

假定在动用LLDB指令中发现有 unknown type
的接近错误(多见于id类型,比如NSArray中有个别值),那我们就不恐怕不显式注明类型。比如下边那一个命令,我们得那样修改。

p (void)NSLog(@"%@",[self.view viewWithTag:1001])

诸如此类就能收获不错的结果了。 此外,lldb是不援救宏的,必要大家团结替换。
找不到艺术
周边于出口frame的时候。比如您或然会获取以下的错误音讯:

(lldb) po self.view.frame
error: unsupported expression with unknown type
error: unsupported expression with unknown type
error: 2 errors parsing expression

那就好像是lldb的三个bug,不能透过点属性访问的章程打framework里面的目标,不过本身在app里面定义的就足以。大家把地方的下令改动一下:

(lldb) p (CGRect)[self.view frame]
(CGRect) $0 = origin=(x=0, y=0) size=(width=320, height=480)

那种方法有它的便宜之处,就是我们不必要太多想想,须求跟踪有个别地方的时候,直接出口就可以博得调试音讯了。但诸如此类做也有它的弊病,就是大家每一次那样调试,都要再三的编译,运行,然后写进新的
print
语句,再持续编译,运行。反复的编译,运维会相比较消耗时间。并且大家再调试完之后,很不难会忘记将调试语句删除,导致比比皆是出口语句遗留再代码中,随着项目标久远进行后,那样会对品种前期的调节造成众多干扰。

expression 修改变量

e 变量 = 0
转自:http://objccn.io/issue-19-2/

转自:http://www.starfelix.com/blog/2014/03/17/lldbdiao-shi-ming-ling-chu-tan/
本文紧如果对八个博客举办了齐心协力,重即使想为本身当作三个记下用。
引进:iOS各个调节技巧豪华套餐
http://www.cnblogs.com/daiweilai/p/4421340.html

并且,当大家想再一次调试这段区域的时候,大家不得不再一次写上那么些输出语句。而有时对于某些复杂一些的调节场景,print
输出那样的办法,往往还无法太好的答应。

thread return 线程重返

不仅仅可以使当前的函数再次回到,而且仍能肆意修改当前函数的重回值,而不管传进来的参数如何。

  • ##### 比如大家有如此3个函数:

func add(a:Int, b:Int) -> Int {
    return a + b;
}

假若断点进入这些函数体的时候,我们举行了 thread return 3
命令,那么不论那时候传进来的八个参数是如何,这几个函数都会脱离执行,并再次回到我们指定的值
3。

那就是说有哪些措施能一蹴即至这几个劳动呢,那就是调节技术,调试器大概在大多数现代的付出条件中都会有,所以,iOS
开发也不例外,Xcode 环境为大家提供的附和调试工具就是 LLDB

打印UI全体层级属性

po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]

初识 LLDB

LLDB 就是 XCode
为我们提供的调剂工具。那么说了这么多,到底怎么样是调节工具呢?
或许说调试器你大概会深感相比素不相识,但说到断点,相信你就会听着相比较熟识了。我们还以刚才我们提到的代码为例:

图片 15

断点

大家在第 23
行左边点击了一下,就创立了二个断点,这时大家再运营那个动用的时候,程序启动到此地就会被断点拦截:

图片 16

断点

并且在 Xcode 的命令行区域,突显了 (lldb) 指示符。

种种视图的标识中,都有3个 16进制的字符串,代表这一个视图的 ID,比如那一个:

UIView: 0x7ffcd2c6dc10

以此 ID 的功能丰硕的兵不血刃,得到了这些 ID,
大家就可以经过这几个命令来拿到这几个视图的引用了:

(lldb) e id $view = (id) 0x7fbd71432590

归纳解释下,通过 expression 命令(那里用缩写格局 e),大家用 View 的
ID 值取得了这几个 View 引用,并将它保存到 $view 变量中。

大家拿到了引用之后,就足以对这一个视图举办过多的操作了,比如我们可以在运行时改变那些视图的背景观:

(lldb) e (void) [$view setBackgroundColor:[UIColor redColor]]

本来,大家运维完那条命令,界面上不会立马反应出来,我们还索要调用那几个命令刷新一下:

(lldb) e (void)[CATransaction flush]

主导调试操作

大家回去最初的标题,假如不行使 print 输出,我们怎么能收获 result
的值吗,那就是大家要探究的断点调试机制了,大家先看一下 XCode
尾部调试区域的多少个按钮:

图片 17

  • 率先个按钮是继续的情致,会让程序从断点处復苏,继续往下运作,咱们点了这么些按钮后,应用就会復苏日常运转状态。
  • 第一个按钮是(Step
    Over),单步执行的情趣,每点那一个按钮几回,程序就会从大家断点早先的地方,向下举行一步。
  • 其五个按钮是 (Step
    In),进入实施的情趣,简而言之就是只要大家当下的断点在3个函数调用上,把么断点会继续进入这几个函数的里边进行调节。
  • 第多个按钮是(Step Out),跳出的趣味,
    就是只要我们近年来再多个函数中,它会跳出当前的函数,回到函数的调用处。

图片 18

恩。。你说了那样多,完全听不懂啊

没什么,大家一一道来,依旧回到大家最初的急需,大家的断点以后停在给
result
变量赋值的这条语句中,断点所在地点的话语是还平素不被实施的,所以大家必要点一下
Step Over
按钮(也等于我们刚刚列出的多少个按钮的第一个),让程序执行一行代码。

施行完这行代码后,大家的 result
变量就被赋值完毕了。那么难题来了,大家怎么获得 result 变量中得内容呢?

还记得大家的 LLDB 命令行么,大家应用二个叫做 po
的命令,就可以取到那个变量:

图片 19

明日,大家使用 LLDB 命令达到了和 print 语句同样的功效,得到了
result
变量的取值。那么难点又来了,那样做有怎样利益吗,怎么感觉比直接拔取
print 输出更麻烦了啊?

图片 20

上面作者就来报告大家原因。

关于 Chisel

末尾,再给我们延展一下。LLDB 自己的命令系统十三分健全,并且它还资助Python
的本子扩充,那样它又有了很正确的增加性,大家可以依照本身的要求来伸张本身的脚本。

Chisel 正是 LLDB 扩张的2个独立例证,那是由 Facebook团队开发的一个开源的 LLDB 的 Python 增加集合,它再 LLDB
命令的底子上,又为咱们提供了尤其便于的操作接口。

  • ##### 比如我们要打印当前的视图层级,若是用 LLDB 原生的通令,大家需要这么:

(lldb) po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]
  • ##### 而 Chisel 为大家提供了更简短的接口:

(lldb) pviews
  • ##### 同样的,那条用于刷新突显的通令:

(lldb) e (void)[CATransaction flush]
  • ##### Chisel 也为大家提供了方便的接口:

(lldb) caflush

关于 Chisel
的越来越多内容,我们可以参照它的主页:https://github.com/facebook/chisel

LLDB
本人完善的命令行系统,以及它的恢宏能力,都改为进步大家付出成效的利器。正确的使用好调试工具,一定会拉扯我们很快的化解更加多的题材。

想追究更加多LLDB命令行, 可经过help一声令下来发现更是多的新陆地~

LLDB 探索之旅

LLDB 为大家提供了成百上千方便使用的吩咐,我们再 LLDB 命令行中,输入
help 命令即可看到这么些命令的赞助新闻:

Debugger commands:

  apropos           -- Find a list of debugger commands related to a particular
                       word/subject.
  breakpoint        -- A set of commands for operating on breakpoints. Also see
                       _regexp-break.
  command           -- A set of commands for managing or customizing the
                       debugger commands.
  disassemble       -- Disassemble bytes in the current function, or elsewhere
                       in the executable program as specified by the user.
  expression        -- Evaluate an expression (ObjC++ or Swift) in the current
                       program context, using user defined variables and
                       variables currently in scope.
  frame             -- A set of commands for operating on the current thread's
                       frames.

  ...............

此处我们见到了 LLDB
命令的列表,要想拿到有个别命令更详尽的协助,大家开可以输入 help 命令名,
比如大家输入 help expression:

help expression
     Evaluate an expression (ObjC++ or Swift) in the current program context,
     using user defined variables and variables currently in scope.  This
     command takes 'raw' input (no need to quote stuff).

Syntax: expression <cmd-options> -- <expr>

Command Options Usage:
  expression [-AFLORTg] [-f <format>] [-G <gdb-format>] [-l <language>] [-a <boolean>] [-i <boolean>] [-t <unsigned-integer>] [-u <boolean>] [-v[<description-verbosity>]] [-d <none>] [-S <boolean>] [-D <count>] [-P <count>] [-Y[<count>]] [-V <boolean>] -- <expr>
  expression [-AFLORTg] [-l <language>] [-a <boolean>] [-i <boolean>] [-t <unsigned-integer>] [-u <boolean>] [-d <none>] [-S <boolean>] [-D <count>] [-P <count>] [-Y[<count>]] [-V <boolean>] -- <expr>

就收获了关于 expression 命令的介绍。

主干情况就说这样多,那么我们就来推行一下,体验一下 LLDB 的强硬之处。

咱俩来看壹个更强大的命令 expression, 我们来看一下它的讲述:

Evaluate an expression (ObjC++ or Swift) in the current program
context,
using user defined variables and variables currently in scope. This
command takes ‘raw’ input (no need to quote stuff).

翻译一下哈,意思就是在眼下程序环境中,执行其余的表明式,并且能够定义和操作已存在的变量。

哪些,让自己说的更具体吧,有了 LLDB
大家不光可以在断点处出口有些变量的值,大家还足以修改甚至重新定义有些变量的值。

我们起始吧,将我们刚刚的主次做一下改动:

var result = parseJSON("[1,2,3]");

if result?.count == 0 {

    print("No Data");

}else{

    print(result);

}

我们那边对 result
变量举办了判断,并开展了个其他出口。上边大家以让将断点设置到第二个语句上,然后运行程序。再断点处大家用
po 命令来打印出 result 的值。

这时候,result 中的值,应该是分析后的 JSON
数组。所以大家苏醒程序执行后,接下去的 if 判断会走第1个分支,输出
result 中的内容。

那就是说一旦我们在刚刚断点时候,运维那些命令呢:

e result = []

此间大家将 result
的值修改为一个空数组,然后我们屡次三番程序,接着你会意识,上面的 if
判断走了第四个分支,约等于说大家在断点处对变量进行的改动,是对全局程序生效的。

怎么这么些能力是大家事先的调节方法不可以达标的啊~

我们地点的 e 命令是 expression 命令的缩写,详情可以参考 LLDB help
命令的鼎力相助。

支配流快捷命令

作者们后续追究,还记得前边我们提到的多少个控制流按钮吗,相当于那张图片:

图片 21

在 LLDB 命令行中,对于每一种流程控制按钮都有相应的一声令下。

  • n 命令,代表 Step Over 操作。
  • s 命令,代表 Step Into 操作。
  • finish 命令,代表 Step Out 操作。
  • c 命令,代表苏醒程序执行操作。

大家依然以那个顺序为例,本次大家使用控制流命令来举行操作:

图片 22

我们运营这么些顺序,然后在断点检测到时,依据上边的顺序输入 LLDB 命令:

s
n
n
c

大家首先个输入的 s 命令,会步入 parseJSON
函数的调用,然后断点就会跻身 parseJSON 函数中。随后,我们又输入了 n
命令,由于 parseJSON 中唯有二个 return 语句,那么控制流就会跳出
parseJSON 函数体,重新回来初阶处。紧接着大家再次输入 n
命令,那时候程序就会将 parseJSON 的结果赋值给 result。最后大家按下
c 命令,来复苏程序的执行。随后的 if
判断中就会依据相应的尺度输出内容了。

如何,那样操作起来就相比便利了,我们不要用鼠标点来点去了,完全用键盘敲命令就足以做到控制流的操作了。

除此以外,除了这个,还有二个特别实用的决定流语句
thread return。那些命令很风趣,它不但可以使方今的函数再次回到,而且还足以随意修改当前函数的重返值,而不管传进来的参数怎么样。比如大家有诸如此类贰个函数:

func add(a:Int, b:Int) -> Int {

    return a + b;

}

就算断点进入这么些函数体的时候,大家执行了 thread return 3
命令,那么不论那时候传进来的三个参数是何等,那些函数都会脱离执行,并赶回大家指定的值
3

恩。。 这一点照旧有个别神奇的~

图片 23

断点创设命令

大家除了通过用鼠标在代码行的左侧点击的法门创制断点以外,大家还足以采取LLDB 来创建断点,比如要创设一个大家事先那样的断点:

图片 24

咱俩得以输入那样一条命令:

(lldb) breakpoint set -f ViewController.swift -l 28
Breakpoint 2: where = Example`Example.ViewController.viewDidLoad (Example.ViewController)() -> () + 478 at ViewController.swift:29, address= 0x000000010f74f61e

输入指令后,紧接着会有一行输出,告诉大家断点成立成功,并且突显了创立的新断点的地方等为主新闻。

本条命令也有简写情势:

b ViewController.swift:28

作者们还是可以将断点直接设置到函数上,假使大家有如此三个函数:

func add(a:Int, b:Int) -> Int {

    return a + b;

}

大家仍可以如此设置断点:

b add

诸如此类就将断点设置再了 add 函数调用的起来地方。

咱俩开可以安装符号断点,比如那样:

b -[NSArray objectAtIndex:]

本条断点会将全部对于 NSArray 的 objectAtIndex
方法的调用设置为断点。这里包含大家开发者对它的调用,以及系统框架之中对它的调用。符号断点对于调试是二个很好用的工具,它可以跟踪那二个我们引用的系统库中的代码出现的题目。

笔者们还是可以对曾经创建的断点设置激发条件:

图片 25

我们地点安装的原则表示,唯有在 resultcount 属性等于 0
的时候,断点才会被鼓舞。

是还是不是觉得 LLDB 有点意思了呢。

刚刚这一长串,给我们介绍了无数 LLDB 的基础内容,相信大家对 LLDB
已经有了1个完好无损的刺探。

始发探险

那就是说以往大家就来用 LLDB 完毕部分特别有意思的业务吗。

咱俩先是创造二个示范项目:

图片 26

品类项目选取 Single View Application

下一场点击 Next, 项目音信中的 Language 选择 Swift:

图片 27

点击 Next
然前面世项目存储地点的取舍,接纳2个你本人的储存地方。接下来大家在
Main.storyboard 中拖放2个 Button 放到右上角:

图片 28

随之,大家按住 Option 键,然后点击 ViewController.swift
文件,可以在统筹界面旁边打开协助界面。打开后,大家按住 Control
键,然后将大家恰好成立的按钮拖动到代码视图中:

图片 29

然后放手鼠标按键,我们就相会到多个弹出菜单:

图片 30

我们将 Connection 的项目拔取为 Action, Name 输入为
buttonClicked,其余不用更改,然后点击 Connect
按钮。这样就成功了按钮事件的开创。

接下去,大家运维应用,就可以看来如此的界面了:

图片 31

全总就绪,今后就足以开展大家的 LLDB 大法啦~

实际上我们还足以不经过断点的办法来打开 LLDB
命令行,在大家先将程序运营起来,然后大家看一下调节区域的按钮:

图片 32

小心下,黄绿的断点开关按钮左边还有1个刹车按钮,我们只须要点那么些暂停按钮,就可以进去
LLDB 命令行调试意况。因为 LLDB 在 Xcode
运维中是一直驻留在后台的,所以大家其实是可以在其它时间都足以运转 LLDB
命令行的。

打开 LLDB
命令行后,大家得以输入那么些命令,打印出当下的视图层级(又学一招~):

(lldb) po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]
<UIWindow: 0x7ffcd2f0f1e0; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x7ffcd2f10170>; layer = <UIWindowLayer: 0x7ffcd2f0ea80>>
   | <UIView: 0x7ffcd2c6dc10; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7ffcd2c17f10>>
   |    | <UIButton: 0x7ffcd2c6dfc0; frame = (20 62; 78 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7ffcd2c6bc10>>
   |    |    | <UIButtonLabel: 0x7ffcd2f15af0; frame = (16 6; 46 18); text = 'Button'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7ffcd2f16120>>
   |    | <_UILayoutGuide: 0x7ffcd2c6fae0; frame = (0 0; 0 20); hidden = YES; layer = <CALayer: 0x7ffcd2c6faa0>>
   |    | <_UILayoutGuide: 0x7ffcd2c70740; frame = (0 667; 0 0); hidden = YES; layer = <CALayer: 0x7ffcd2c6d3d0>>

大家细心看一下,每一种视图的标识中,都有二个16进制的字符串,代表那一个视图的 ID,比如那几个:

UIView: 0x7ffcd2c6dc10

本条 ID 的机能丰富的强劲,拿到了那几个 ID,
大家就足以经过这些命令来博取这么些视图的引用了:

(lldb) e id $view = (id) 0x7fbd71432590

简言之表达下,通过 expression命令(那里用缩写格局 e),大家用 View 的 ID
值取得了那几个 View 引用,并将它保存到 $view 变量中。

大家赢得了引用之后,就足以对这几个视图举办过多的操作了,比如大家得以在运作时改变那几个视图的背景观:

(lldb) e (void) [$view setBackgroundColor:[UIColor redColor]]

本来,大家运维完那条命令,界面上不会立即反应出来,大家还索要调用那个命令刷新一下:

(lldb) e (void)[CATransaction flush]

这么,我们在看一下大家运行的程序,主界面的背景象变成紫褐了呢。

我们竟然还能用它来找到有些控件上助长的风浪,大家找到大家温馨丰裕的
UIBUtton 的 ID:

UIButton: 0x7ffcd2c6dfc0

接下来运维下边的下令:

(lldb) e id $button = (id) 0x7ffcd2c6dfc0

(lldb) po [$button  allTargets]
{(
    <lldb.ViewController: 0x7feff2d67330>
)}

我们取得 UIButton 的引用后,然后又输出了他的 allTargets
属性,得到了那几个 UIButton 所对应的风浪 target
对象的地方,接下去大家再用刚刚拿到的这么些 target 地址获取它的 action
属性:

(lldb) po [$button actionsForTarget:(id)0x7feff2d67330 forControlEvent:0]
<__NSArrayM 0x7feff2c22350>(
buttonClicked:
)

咱俩这么就拿到了,那个按钮所对应的方法名了。那么接下去,大家得以在那个法子上安装断点,只怕用
LLDB
的运作时能力替换这么些点子的落实等等。显而易见,对于大家调试应用来说,LLDB
是1个不胜强劲而火速的工具。那里只介绍了它的冰山一角,关于越多的始末,我们可以采取
help
命令,进行长远的钻研。相信大家表明聪明才智,可以察觉更加多它的强大之处。

一点点延展,关于 Chisel

末段,再给大家延展一下。LLDB 本人的下令系统丰硕健全,并且它还帮助 Python
的剧本伸张,那样它又有了很不错的扩充性,我们得以依照本人的须要来扩展本人的本子。

Chisel
正是 LLDB 伸张的2个典型例证,那是由 脸书 团队开发的三个开源的 LLDB
的 Python 扩大集合,它再 LLDB
命令的功底上,又为我们提供了更进一步惠及的操作接口。

譬如说大家要打印当前的视图层级,如若用 LLDB 原生的指令,大家需求这么:

(lldb) po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]

而 Chisel 为我们提供了更简短的接口:

(lldb) pviews

平等的,这条用于刷新彰显的命令:

(lldb) e (void)[CATransaction flush]

Chisel 也为大家提供了便捷的接口:

(lldb) caflush

此地只给我们做三个大概的牵线,关于
Chisel
的越来越多内容,我们可以参考它的主页:https://github.com/facebook/chisel

LLDB
本身完善的命令行系统,以及它的恢宏能力,都变成升级我们付出效用的利器。正确的行使好调试工具,一定会资助大家疾速的消除越多的标题。

近年来,通过 help 命令,来发轫对 LLDB
命令行的切磋吧,相信你能在此间发现越来越多的财富。