一、expression
expression命令的作用是执行一个表达式,并将表达式返回的结果输出。expression的完整语法是这样的:
expression <cmd-options> -- <expr>
1.<cmd-options>:命令选项,一般情况下使用默认的即可,不需要特别标明。
2.--: 命令选项结束符,表示所有的命令选项已经设置完毕,如果没有命令选项,--可以省略
3.<expr>: 要执行的表达式
说expression是LLDB里面最重要的命令都不为过。因为他能实现2个功能。
- 执行某个表达式。
我们在代码运行过程中,可以通过执行某个表达式来动态改变程序运行的轨迹。
假如我们在运行过程中,突然想把self.view颜色改成红色,看看效果。我们不必写下代码,重新run,只需暂停程序,用expression改变颜色,再刷新一下界面,就能看到效果
// 改变颜色
(lldb) expression -- self.view.backgroundColor = [UIColor redColor]
// 刷新界面
(lldb) expression -- (void)[CATransaction flush]
- 将返回值输出。
也就是说我们可以通过expression来打印东西。
假如我们想打印self.view:
(lldb) expression -- self.view
(UIView *) $1 = 0x00007fe322c18a10
二、p & print & call
1.print: 打印某个东西,可以是变量和表达式
2.p: 可以看做是print的简写
3.call: 调用某个方法。
表面上看起来他们可能有不一样的地方,实际都是执行某个表达式(变量也当做表达式),将执行的结果输出到控制台上。所以你可以用p调用某个方法,也可以用call打印东西
e.g: 下面代码效果相同:
(lldb) expression -- self.view
(UIView *) $5 = 0x00007fb2a40344a0
(lldb) p self.view
(UIView *) $6 = 0x00007fb2a40344a0
(lldb) print self.view
(UIView *) $7 = 0x00007fb2a40344a0
(lldb) call self.view
(UIView *) $8 = 0x00007fb2a40344a0
(lldb) e self.view
(UIView *) $9 = 0x00007fb2a40344a0
三、po
OC里所有的对象都是用指针表示的,所以一般打印的时候,打印出来的是对象的指针,而不是对象本身。如果我们想打印对象。我们需要使用命令选项:-O。为了更方便的使用,LLDB为expression -O --定义了一个别名:po
(lldb) expression -- self.view
(UIView *) $13 = 0x00007fb2a40344a0
(lldb) expression -O -- self.view
<UIView: 0x7fb2a40344a0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fb2a4018c80>>
(lldb) po self.view
<UIView: 0x7fb2a40344a0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fb2a4018c80>>
四、thread(thread backtrace & bt)
当发生crash的时候,我们可以使用thread backtrace查看堆栈调用
(lldb) thread backtrace
* thread #1: tid = 0xdd42, 0x000000010afb380b libobjc.A.dylib`objc_msgSend + 11, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
frame #0: 0x000000010afb380b libobjc.A.dylib`objc_msgSend + 11
* frame #1: 0x000000010aa9f75e TLLDB`-[ViewController viewDidLoad](self=0x00007fa270e1f440, _cmd="viewDidLoad") + 174 at ViewController.m:23
frame #2: 0x000000010ba67f98 UIKit`-[UIViewController loadViewIfRequired] + 1198
frame #3: 0x000000010ba682e7 UIKit`-[UIViewController view] + 27
frame #4: 0x000000010b93eab0 UIKit`-[UIWindow addRootViewControllerViewIfPossible] + 61
frame #5: 0x000000010b93f199 UIKit`-[UIWindow _setHidden:forced:] + 282
frame #6: 0x000000010b950c2e UIKit`-[UIWindow makeKeyAndVisible] + 42
我们可以看到crash发生在-[ViewController viewDidLoad]中的第23行,只需检查这行代码是不是干了什么非法的事儿就可以了。
LLDB还为backtrace专门定义了一个别名:bt,他的效果与thread backtrace相同,如果你不想写那么长一串字母,直接写下bt即可:
(lldb) bt
五、thread return
Debug的时候,也许会因为各种原因,我们不想让代码执行某个方法,或者要直接返回一个想要的值。这时候就该thread return上场了。
e.g: 我们有一个someMethod方法,默认情况下是返回YES。我们想要让他返回NO
(lldb) thread return NO
六、c & n & s & finish
用触摸板的孩子们可能会觉得点击这4个按钮比较费劲。其实LLDB命令也可以完成上面的操作,而且如果不输入命令,直接按Enter键,LLDB会自动执行上次的命令。按一下Enter就能达到我们想要的效果,有木有顿时感觉逼格满满的!!!
我们来看看对应这4个按钮的LLDB命令:
1.c/ continue/ thread continue: 这三个命令效果都等同于上图中第一个按钮的。表示程序继续运行
2.n/ next/ thread step-over: 这三个命令效果等同于上图第二个按钮。表示单步运行
3.s/ step/ thread step-in: 这三个命令效果等同于上图第三个按钮。表示进入某个方法
4.finish/ step-out: 这两个命令效果等同于第四个按钮。表示直接走完当前方法,返回到上层frame
七、frame variable
前面我们提到过很多次frame(帧)。可能有的朋友对frame这个概念还不太了解。随便打个断点
我们在控制台上输入命令bt,可以打印出来所有的frame。如果仔细观察,这些frame和左边红框里的堆栈是一致的。平时我们看到的左边的堆栈就是frame。
平时Debug的时候我们经常做的事就是查看变量的值,通过frame variable命令,可以打印出当前frame的所有变量
(lldb) frame variable
(ViewController *) self = 0x00007fa158526e60
(SEL) _cmd = "text:"
(BOOL) ret = YES
(int) a = 3
八、breakpoint
九、watchpoint
breakpoint有一个孪生兄弟watchpoint。如果说breakpoint是对方法生效的断点,watchpoint就是对地址生效的断点
如果我们想要知道某个属性什么时候被篡改了,我们该怎么办呢?有人可能会说对setter方法打个断点不就行了么?但是如果更改的时候没调用setter方法呢?
这时候最好的办法就是用watchpoint。我们可以用他观察这个属性的地址。如果地址里面的东西改变了,就让程序中断
- watchpoint set variable
一般情况下,要观察变量或者属性,使用watchpoint set variable命令即可
e.g: 观察self->_string
(lldb) watchpoint set variable self->_string
Watchpoint created: Watchpoint 1: addr = 0x7fcf3959c418 size = 8 state = enabled type = w
watchpoint spec = 'self->_string'
new value: 0x0000000000000000
- watchpoint set expression
如果我们想直接观察某个地址,可以使用watchpoint set expression
e.g: 我们先拿到_model的地址,然后对地址设置一个watchpoint
(lldb) p &_model
(Modek **) $3 = 0x00007fe0dbf23280
(lldb) watchpoint set expression 0x00007fe0dbf23280
Watchpoint created: Watchpoint 1: addr = 0x7fe0dbf23280 size = 8 state = enabled type = w
new value: 0
十、target
对于target这个命令,我们用得最多的可能就是target modules lookup。由于LLDB给target modules取了个别名image,所以这个命令我们又可以写成image lookup。
- image lookup --address
当我们有一个地址,想查找这个地址具体对应的文件位置,可以使用image lookup --address,简写为image lookup -a
e.g: 当我们发生一个crash
2015-12-17 14:51:06.301 TLLDB[25086:246169] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArray0 objectAtIndex:]: index 1 beyond bounds for empty NSArray'
*** First throw call stack:
(
0 CoreFoundation 0x000000010accde65 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x000000010a746deb objc_exception_throw + 48
2 CoreFoundation 0x000000010ac7c395 -[__NSArray0 objectAtIndex:] + 101
3 TLLDB 0x000000010a1c3e36 -[ViewController viewDidLoad] + 86
4 UIKit 0x000000010b210f98 -[UIViewController loadViewIfRequired] + 1198
5 UIKit 0x000000010b2112e7 -[UIViewController view] + 27
我们可以看到是由于-[__NSArray0 objectAtIndex:]:超出边界而导致的crash,但是objectAtIndex:的代码到底在哪儿呢?
(lldb) image lookup -a 0x000000010a1c3e36
Address: TLLDB[0x0000000100000e36] (TLLDB.__TEXT.__text + 246)
Summary: TLLDB`-[ViewController viewDidLoad] + 86 at ViewController.m:32
根据0x000000010a1c3e36 -[ViewController viewDidLoad]里面的地址,使用image lookup --address查找,我们可以看到代码位置在ViewController.m里面的32行
- image lookup --name
当我们想查找一个方法或者符号的信息,比如所在文件位置等。我们可以使用image lookup --name,简写为image lookup -n。
(lldb) image lookup -n dictionaryWithXMLString:
2 matches found in /Users/jiangliancheng/Library/Developer/Xcode/DerivedData/VideoIphone-aivsnqmlwjhxapdlvmdmrubbdxpq/Build/Products/Debug-iphoneos/BaiduIphoneVideo.app/BaiduIphoneVideo:
Address: BaiduIphoneVideo[0x00533a7c] (BaiduIphoneVideo.__TEXT.__text + 5414908)
Summary: BaiduIphoneVideo`+[NSDictionary(SAPIXmlDictionary) dictionaryWithXMLString:] at XmlDictionary.m
Module: file = "/Users/jiangliancheng/Library/Developer/Xcode/DerivedData/VideoIphone-aivsnqmlwjhxapdlvmdmrubbdxpq/Build/Products/Debug-iphoneos/BaiduIphoneVideo.app/BaiduIphoneVideo", arch = "armv7"
CompileUnit: id = {0x00000000}, file = "/Users/jiangliancheng/Development/Work/iOS_ShareLib/SharedLib/Srvcs/BDPassport4iOS/BDPassport4iOS/SAPI/Extensive/ThirdParty/XMLDictionary/XmlDictionary.m", language = "Objective-C"
Function: id = {0x23500000756}, name = "+[NSDictionary(SAPIXmlDictionary) dictionaryWithXMLString:]", range = [0x005a6a7c-0x005a6b02)
FuncType: id = {0x23500000756}, decl = XmlDictionary.m:189, clang_type = "NSDictionary *(NSString *)"
Blocks: id = {0x23500000756}, range = [0x005a6a7c-0x005a6b02)
LineEntry: [0x005a6a7c-0x005a6a98): /Users/jiangliancheng/Development/Work/iOS_ShareLib/SharedLib/Srvcs/BDPassport4iOS/BDPassport4iOS/SAPI/Extensive/ThirdParty/XMLDictionary/XmlDictionary.m
Symbol: id = {0x0000f2d5}, range = [0x005a6a7c-0x005a6b04), name="+[NSDictionary(SAPIXmlDictionary) dictionaryWithXMLString:]"
Variable: id = {0x23500000771}, name = "self", type = "Class", location = [sp+32], decl =
Variable: id = {0x2350000077e}, name = "_cmd", type = "SEL", location = [sp+28], decl =
Variable: id = {0x2350000078b}, name = "string", type = "NSString *", location = [sp+24], decl = XmlDictionary.m:189
Variable: id = {0x23500000799}, name = "data", type = "NSData *", location = [sp+20], decl = XmlDictionary.m:192
Address: BaiduIphoneVideo[0x012ee160] (BaiduIphoneVideo.__TEXT.__text + 19810016)
Summary: BaiduIphoneVideo`+[NSDictionary(XMLDictionary) dictionaryWithXMLString:] at XMLDictionary.m
Module: file = "/Users/jiangliancheng/Library/Developer/Xcode/DerivedData/VideoIphone-aivsnqmlwjhxapdlvmdmrubbdxpq/Build/Products/Debug-iphoneos/BaiduIphoneVideo.app/BaiduIphoneVideo", arch = "armv7"
CompileUnit: id = {0x00000000}, file = "/Users/wingle/Workspace/qqlive4iphone/iphone_4.0_fabu_20150601/Common_Proj/mobileTAD/VIDEO/Library/Third Party/XMLDictionary/XMLDictionary.m", language = "Objective-C"
Function: id = {0x79900000b02}, name = "+[NSDictionary(XMLDictionary) dictionaryWithXMLString:]", range = [0x01361160-0x0136119a)
FuncType: id = {0x79900000b02}, decl = XMLDictionary.m:325, clang_type = "NSDictionary *(NSString *)"
Blocks: id = {0x79900000b02}, range = [0x01361160-0x0136119a)
LineEntry: [0x01361160-0x01361164): /Users/wingle/Workspace/qqlive4iphone/iphone_4.0_fabu_20150601/Common_Proj/mobileTAD/VIDEO/Library/Third Party/XMLDictionary/XMLDictionary.m
Symbol: id = {0x0003a1e9}, range = [0x01361160-0x0136119c), name="+[NSDictionary(XMLDictionary) dictionaryWithXMLString:]"
东西有点多,我们只需关注里面的file这一行:
CompileUnit: id = {0x00000000}, file = "/Users/jiangliancheng/Development/Work/iOS_ShareLib/SharedLib/Srvcs/BDPassport4iOS/BDPassport4iOS/SAPI/Extensive/ThirdParty/XMLDictionary/XmlDictionary.m", language = "Objective-C"
CompileUnit: id = {0x00000000}, file = "/Users/wingle/Workspace/qqlive4iphone/iphone_4.0_fabu_20150601/Common_Proj/mobileTAD/VIDEO/Library/Third Party/XMLDictionary/XMLDictionary.m", language = "Objective-C"
可以清晰的看到,LLDB给我们找出来了这个方法的位置。
当然这个命令也可以找到方法的其他相关信息,比如参数等.
- image lookup --type
当我们想查看一个类型的时候,可以使用image lookup --type,简写为image lookup -t:
e.g: 我们来看看Model的类型:
(lldb) image lookup -t Model
Best match found in /Users/jiangliancheng/Library/Developer/Xcode/DerivedData/TLLDB-beqoowskwzbttrejseahdoaivpgq/Build/Products/Debug-iphonesimulator/TLLDB.app/TLLDB:
id = {0x30000002f}, name = "Model", byte-size = 32, decl = Modek.h:11, clang_type = "@interface Model : NSObject{
NSString * _bb;
NSString * _cc;
NSString * _name;
}
@property ( getter = name,setter = setName:,readwrite,nonatomic ) NSString * name;
@end
"
可以看到,LLDB把Model这个class的所有属性和成员变量都打印了出来,当我们想了解某个类的时候,直接使用image lookup -t即可
十一、Extension
给UIApplicationMain设置一个断点,在断点中添加执行e @import UIKit。
(lldb) p self.view.frame
error: property 'frame' not found on object of type 'UIView *'
error: 1 errors parsing expression
(lldb) e @import UIKit(不需要写)
(lldb) p self.view.frame
(CGRect) $0 = (origin = (x = 0, y = 0), size = (width = 375, height = 667))