本文主要介绍以下几个内容:
1.NSLog使用及上线版本设置
2.运行中任意位置打印日志 : LLDB的po(打印对象),p(基础数据),image(崩溃调试地址信息)等等命令使用
3.运行中随时修改条件执行断点:Breakpoint
4.全局断点模式设置
5.其他
一 : NSLog使用及上线版本设置: 考虑到NSLog会影响到性能,发布版本还是尽量不要打印,那么就要区分版本,
在项目头文件添加下面一段代码即可,项目中所有的 NSLog 在真机运行都不会执行了
#if TARGET_IPHONE_SIMULATOR
#define DEBUG 1 //模拟环境
#elif TARGET_OS_IPHONE
#define DEBUG 0 //真机
#endif
#ifdef DEBUG
# define NSLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#else
# define NSLog(...)
#endif
二 : 运行中任意位置打印日志 : LLDB的po,p,call 等命令使用
下面简单举一个测试例子:设置一个断点,运行到此截断后,在xcode底部右侧(lldb) 后开始输入命令" po 对象 ",然后回车即可

当你使用po命令后一定程度上调试可以不用NSLog了吧.
那么lldb到底有多少命令呢? 输入help回车告诉你有很多commands:

Debugger commands:
apropos -- List debugger commands related to a word or subject.
breakpoint -- Commands for operating on breakpoints (see 'help b' for
shorthand.)
bugreport -- Commands for creating domain-specific bug reports.
command -- Commands for managing custom LLDB commands.
disassemble -- Disassemble specified instructions in the current
target. Defaults to the current function for the
current thread and stack frame.
expression -- Evaluate an expression on the current thread. Displays
any returned value with LLDB's default formatting.
frame -- Commands for selecting and examing the current thread's
stack frames.
gdb-remote -- Connect to a process via remote GDB server. If no host
is specifed, localhost is assumed.
gui -- Switch into the curses based GUI mode.
help -- Show a list of all debugger commands, or give details
about a specific command.
kdp-remote -- Connect to a process via remote KDP server. If no UDP
port is specified, port 41139 is assumed.
language -- Commands specific to a source language.
log -- Commands controlling LLDB internal logging.
memory -- Commands for operating on memory in the current target
process.
platform -- Commands to manage and create platforms.
plugin -- Commands for managing LLDB plugins.
process -- Commands for interacting with processes on the current
platform.
quit -- Quit the LLDB debugger.
register -- Commands to access registers for the current thread and
stack frame.
script -- Invoke the script interpreter with provided code and
display any results. Start the interactive interpreter
if no code is supplied.
settings -- Commands for managing LLDB settings.
source -- Commands for examining source code described by debug
information for the current target process.
target -- Commands for operating on debugger targets.
thread -- Commands for operating on one or more threads in the
current process.
type -- Commands for operating on the type system.
version -- Show the LLDB debugger version.
watchpoint -- Commands for operating on watchpoints.
Current command abbreviations (type 'help command alias' for more info):
add-dsym -- Add a debug symbol file to one of the target's current modules
by specifying a path to a debug symbols file, or using the
options to specify a module to download symbols for.
attach -- Attach to process by ID or name.
b -- Set a breakpoint using one of several shorthand formats.
bt -- Show the current thread's call stack. Any numeric argument
displays at most that many frames. The argument 'all' displays
all threads.
c -- Continue execution of all threads in the current process.
call -- Evaluate an expression on the current thread. Displays any
returned value with LLDB's default formatting.
continue -- Continue execution of all threads in the current process.
detach -- Detach from the current target process.
di -- Disassemble specified instructions in the current target.
Defaults to the current function for the current thread and
stack frame.
dis -- Disassemble specified instructions in the current target.
Defaults to the current function for the current thread and
stack frame.
display -- Evaluate an expression at every stop (see 'help target
stop-hook'.)
down -- Select a newer stack frame. Defaults to moving one frame, a
numeric argument can specify an arbitrary number.
env -- Shorthand for viewing and setting environment variables.
exit -- Quit the LLDB debugger.
f -- Select the current stack frame by index from within the current
thread (see 'thread backtrace'.)
file -- Create a target using the argument as the main executable.
finish -- Finish executing the current stack frame and stop after
returning. Defaults to current thread unless specified.
image -- Commands for accessing information for one or more target
modules.
j -- Set the program counter to a new address.
jump -- Set the program counter to a new address.
kill -- Terminate the current target process.
l -- List relevant source code using one of several shorthand formats.
list -- List relevant source code using one of several shorthand formats.
n -- Source level single step, stepping over calls. Defaults to
current thread unless specified.
next -- Source level single step, stepping over calls. Defaults to
current thread unless specified.
nexti -- Instruction level single step, stepping over calls. Defaults to
current thread unless specified.
ni -- Instruction level single step, stepping over calls. Defaults to
current thread unless specified.
p -- Evaluate an expression on the current thread. Displays any
returned value with LLDB's default formatting.
parray -- Evaluate an expression on the current thread. Displays any
returned value with LLDB's default formatting.
po -- Evaluate an expression on the current thread. Displays any
returned value with formatting controlled by the type's author.
poarray -- Evaluate an expression on the current thread. Displays any
returned value with LLDB's default formatting.
print -- Evaluate an expression on the current thread. Displays any
returned value with LLDB's default formatting.
q -- Quit the LLDB debugger.
r -- Launch the executable in the debugger.
rbreak -- Sets a breakpoint or set of breakpoints in the executable.
repl -- Evaluate an expression on the current thread. Displays any
returned value with LLDB's default formatting.
run -- Launch the executable in the debugger.
s -- Source level single step, stepping into calls. Defaults to
current thread unless specified.
si -- Instruction level single step, stepping into calls. Defaults to
current thread unless specified.
sif -- Step through the current block, stopping if you step directly
into a function whose name matches the TargetFunctionName.
step -- Source level single step, stepping into calls. Defaults to
current thread unless specified.
stepi -- Instruction level single step, stepping into calls. Defaults to
current thread unless specified.
t -- Change the currently selected thread.
tbreak -- Set a one-shot breakpoint using one of several shorthand
formats.
undisplay -- Stop displaying expression at every stop (specified by stop-hook
index.)
up -- Select an older stack frame. Defaults to moving one frame, a
numeric argument can specify an arbitrary number.
x -- Read from the memory of the current target process.
For more information on any command, type 'help <command-name>'.
看不到解释的话把这段英文Google 翻译一下就行了,建议大部分命令一个一个测试一下,以后需要使用的时候有影响.
下面介绍几个命令:
p - 基础数据类型打印
(lldb) p btn2.tag
(NSInteger) $2 = 0
call - 执行一段代码 比如
(lldb) call 1+2+3
(int) $1 = 6
bt - 显示当前线程的调用堆栈。bt all 显示所有线程堆栈信息。
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
* frame #0: 0x00000001058e42bc ARKit-OC`-[ViewController viewDidLoad](self=0x00007f9031f0b580, _cmd="viewDidLoad") at ViewController.m:31
frame #1: 0x0000000108ef88a5 UIKit`-[UIViewController loadViewIfRequired] + 1235 等等
q - 简单,就是退出断点
e - 设置常量,方便接下来调试用(注意: 声明和使用时对象名称前要加$):
(lldb) e NSArray *$testArray = @[@"A",@"B"]
(lldb) po $testArray[1]
B
(lldb)
由于image跟崩溃调试有关,下面简单介绍一下image
image - Commands for accessing information for one or more target
modules.
image - 地址信息
NSArray *TestArray = @[];
NSLog(@"%@",TestArray[1]);//越界测试
一般项目开启全局断点,会自动打印崩溃信息及位置,如下:

xcode底部日志窗口:

如果不开启全局断点All Exceptions,就会打印下面日志:
*** First throw call stack:
(
0 CoreFoundation 0x00000001088591ab __exceptionPreprocess + 171
1 libobjc.A.dylib 0x0000000107c86f41 objc_exception_throw + 48
2 CoreFoundation 0x0000000108871f3d -[__NSArray0 objectAtIndex:] + 93
3 ARKit-OC 0x00000001073782a3 -[ViewController viewDidLoad] + 1571
4 UIKit 0x00000001094998a5 -[UIViewController loadViewIfRequired] + 1235
5 UIKit 0x00000001094e11ec -[UINavigationController _updateScrollViewFromViewController:toViewController:] + 68
6 UIKit 0x00000001094e1528 -[UINavigationController _startTransition:fromViewController:toViewController:] + 153
7 UIKit 0x00000001094e263f -[UINavigationController _startDeferredTransitionIfNeeded:] + 841
8 UIKit 0x00000001094e38c3 -[UINavigationController __viewWillLayoutSubviews] + 150
9 UIKit 0x000000010973ca49 -[UILayoutContainerView layoutSubviews] + 231
10 UIKit 0x00000001093c56f5 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1439
11 QuartzCore 0x000000010d88b3ee -[CALayer layoutSublayers] + 153
12 QuartzCore 0x000000010d88f4dd _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 401
13 QuartzCore 0x000000010d817ded _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 365
14 QuartzCore 0x000000010d843704 _ZN2CA11Transaction6commitEv + 500
15 UIKit 0x000000010930fd3a __34-[UIApplication _firstCommitBlock]_block_invoke_2 + 141
16 CoreFoundation 0x00000001087fc18c __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12
17 CoreFoundation 0x00000001087e096b __CFRunLoopDoBlocks + 203
18 CoreFoundation 0x00000001087e0144 __CFRunLoopRun + 1300
19 CoreFoundation 0x00000001087df9b9 CFRunLoopRunSpecific + 409
20 GraphicsServices 0x00000001102c79c6 GSEventRunModal + 62
21 UIKit 0x00000001092f45e8 UIApplicationMain + 159
22 ARKit-OC 0x0000000107379f3f main + 111
23 libdyld.dylib 0x000000010c80dd81 start + 1
24 ??? 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
看到这一堆日志新手可能有点蒙,其实这是记录崩溃前后信息,也就是从24 -> 0顺序执行的:
比如23行start开始 -> 22 行 执行main.m里的main函数 -> 等等
所以我们从0开始看,发现第2, 3行比较熟悉(2 objectAtIndex 一般是导致数组越界):
2 CoreFoundation 0x0000000108871f3d -[__NSArray0 objectAtIndex:] + 93
3 ARKit-OC 0x00000001073782a3 -[ViewController viewDidLoad] + 1571
依次是: 序号 项目名称 地址 执行的方法 数字
下面使用image查看该地址0x0000000108871f3d 和 0x00000001073782a3信息:
(lldb) image lookup --address 0x0000000108871f3d
Address: CoreFoundation[0x0000000000110f3d] (CoreFoundation.__TEXT.__text + 1113661)
Summary: CoreFoundation`-[__NSArray0 objectAtIndex:] + 93
(lldb) image lookup --address 0x00000001073782a3
Address: ARKit-OC[0x00000001000022a3] (ARKit-OC.__TEXT.__text + 3459)
Summary: ARKit-OC`-[ViewController viewDidLoad] + 1571 at ViewController.m:40
(lldb)
显然最后一行:at ViewController.m:40 告诉我们崩溃位置.
(其他很多有用的命令自行百度或者Google)
三 : 条件执行断点:Edit Breakpoint
看截图:

弹出下面编辑框:

Condition:输入判断表达式,运行到此判断为true时触发断点
Ignore:设置一个值,比如4,前4次判断会被忽略,到第5次再触发断点
Action:触发断点后执行动作:


AppleScript:执行一段脚本代码
Capture GPU Frame:当前GPU绘制帧
Debugger Command:lldb调试命令
Log Message:打印信息
Shell Command:选择shell命令文件,异步执行的
Sound:播放音乐
[动手试试上面命令吧!]
四 : 全局断点模式设置

(1) Swift Error breakpoint :Swift语言的断点
(2)Exception breakpoint: 全局断点(OC 和 C++语言)
(3)Symbolic breakpoint:符号断点
...
4.0 (1) Swift Error breakpoint :对应Swift语言的断点
4.1 下面介绍一下(2)Exception breakpoint: 全局断点

(2.1) 断点语言:OC 和 C++,有时候出问题的代码在第三方或混编C代码,这个时候需要选择C++进行调试

异常处理机制四个关键字:@try,@catch,@thorw,@finally。
1.当代码有可能出现异常时,我们把他放到@try语句块中。
2.@catch()块包含了处理@try块里的抛出的异常的逻辑。
3.如果直接使用@throw块来抛出异常,这个异常本质上是一个OC的对象。咱们可以使用NSException对象,但是不局限于他们。
4.无论异常是否发生,@finally块里面的语句都会执行。

4.2 下面介绍一下(3)Symbolic breakpoint:符号断点
在Symbol一栏输入需要断点对应的方法

网友评论