LLDB调试命令初探

../art/lldb_in_xc5_command_window_2x.png
若是你在平时的开发中从未使用过调试器,那你恐怕不知道一个调试器的做用有多大。你可能只知足于经过printf或者NSLog输出信息用于调试。但你只要试着尝试在调试中开始使用调试器LLDB,你会立刻感觉到调试器给你带来的便利。
LLDBLLVM下的调试器。Xcode从4.0开始编译器开始改用LLVM,相应的调试器也从gdb改成LLDB。而从 Xcode5.0开始全部工程也被自动设置为使用LLDB。下面本文从初学者的角度讲解在平常的开发中如何使用LLDB以及LLDB经常使用的命令。 html

初识LLDB

你可能从未使用过LLDB,那让咱们先来热热身。 在调试器中最经常使用到的命令是p(用于输出基本类型)或者po(用于输出 Objective-C 对象)。以下,你能够经过输入po 和 view 来输出 view 的信息: c++

po [self view]

随后调试器会输出这个 object 的 description。在这个例子中多是这样的信息: git

(UIView *) $1 = 0x0824c800 <UITableView: 0x824c800; frame = (0 20; 768 1004); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x74c3010>; layer = <CALayer: 0x74c2710>; contentOffset: {0, 0}>

什么?在什么地方能够输入这个命令?OK,首先,咱们须要先设置一个断点。以下图所示,我在viewDidLoad:中设置了一个了一个断点: github

图二:断点图

接下来运行程序,而后程序会停留在断点处,从下图你能够看到在什么地方输入LLDB命令: express

图三:输入命令位置

你可能须要的是 view 下 subview 的数量。因为 subview 的数量是一个 int 类型的值,因此咱们使用命令p: app

p (int)[[[self view] subviews] count]

最后你看到的输出会是: oop

(int) $2 = 2

是否是很简单?
细心的朋友可能会发现输出的信息中带有$一、$2的字样。实际上,咱们每次查询的结果会保存在一些持续变量中($[0-9]+),这样你能够在后面的查询中直接使用这些值。好比如今我接下来要从新取回$1的值: 学习

(lldb) po $1
(UIView *) $1 = 0x0824c800 <UITableView: 0x824c800; frame = (0 20; 768 1004); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x74c3010>; layer = <CALayer: 0x74c2710>; contentOffset: {0, 0}>

能够看到,咱们依然能够取到以前[self view]的值。 ui

LLDB命令还能够用在断点上,详细的使用能够参见这个文章 spa

经常使用命令

下面补充说明其它一些经常使用的命令:

  • expr

能够在调试时动态执行指定表达式,并将结果打印出来。经常使用于在调试过程当中修改变量的值。
图四: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]);

这段代码有明显的错误,程序运行这段代码后会抛出下面的异常:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
*** 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

如今,咱们怀疑出错的地址是0x0000000100004af8(能够根据执行文件名判断,或者最小的栈地址)。为了进一步精肯定位,咱们能够输入如下的命令:

image lookup --address 0x0000000100004af8

命令执行后返回:

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

咱们能够看到,出错的位置是RootViewController.m的第53行。


更多的命令能够参见这个网址
另外,facebook开源了他们扩展的LLDB命令库,有兴趣的朋友也能够安装看看。

简称和别名

不少时候,LLDB完整的命令是很长的。好比前面所说的image lookup --address这个组合命令。为了方便平常的使用,提升效率,LLDB命令也提供经过简称的方式调用命令。仍是这个命令,咱们用简称就能够写为im loo -a,是否是简单多了。
若是你是从gdb时代就开始使用调试器的,你会发现,有些命令如p、call等命令和gdb下是一致的。其实这些命令是LLDB一些命令的别名,好比p是frame variable的别名,p view其实是frame variable view。除了系统自建的LLDB别名,你也能够自定义别名。好比下面这个命令

command alias ioa image lookup --address %1

是将我前面所介绍过的一个命令image lookup --address添加了一个ioa的别名。而后执行下面的命令:

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

能够看到,咱们获得了咱们想要的结果,而命令却大大缩短。 
这里我就再也不详细展开,有兴趣的朋友能够查看这个网址

常见问题

上面咱们简单的学习了如何使用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的时候。好比你可能会获得如下的错误信息:

1 2 3 4
(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里面定义的就能够。咱们把上面的命令改动一下:

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

总结

经过上面一些简单的讲解,相信朋友们已经知道如何使用LLDB命令来提升本身的效率了。Enjoy it!

相关文章
相关标签/搜索