|
当时刚开始搞iOS开发的时候,对断点调试的理解局限于:看代码风骚的走位,即当运行出来的效果对不住我的代码时,我会去看代码是怎么运行的,然后用雍正之剑去砍杀八阿哥。后面慢慢的接触lldb后,发现lldb用起来真的很赞,下面我来分享一下我在平时开发中积累的一些用法,这些用法有些是从网上发现的,有些是同事告诉我的。 相关用法 打印网络请求的相关信息现在开发项目中没有网络请求都不好意思说自己在搞项目了,那为了方便调试,是不是要把请求的网址、请求的参数、服务器返回的数据等相关打印出来呢?我以前的做法都是在每个请求的API方法里将这些信息NSLog打印出来,直到最近我的主管告诉了我一个秘密。在网络请求库收到data的方法里面添加Debugger Command类型的断点。 - po [connection.currentRequest.URL absoluteString]0 y2 P. Y4 X8 h* F' j3 e4 i& d; T
- po [NSString stringWithCString:(char*)[connection.currentRequest.HTTPBody bytes] encoding:4]
+ |+ q& s* \ o9 F5 K - po [NSString stringWithCString:(char*)[data bytes] encoding:4]
复制代码其实上面打印的东西我们都知道,我只是把他们放在了一个断点里面,让可以进行三连击:1、输出请求网址;2、输出请求参数;3、输出返回数据。 这样不仅解决了NSLog打印出来的中文数据显示为UTF8格式,还能省下我们很多代码量,是不是很nice。。。 注:AF接收数据的方法 在AFURLConnectionOperation文件 - - (void)connection:(NSURLConnection __unused *)connection* T8 E! F. a, q# L4 G/ N6 [1 `
- didReceiveData:(NSData *)data)
复制代码 显示图片当你创建UIImage对象从服务器端获取数据时,假如中间出了一点小问题,为了验证图片是否创建成功,可以试一下下面的debug方法。 用将鼠标放在UIImage对象上面,然后按option键,在出现的弹出视图上面,点击像眼睛一样的图标,你可以看到:
Image1 那么图片就显示出来了,并且你还可以选择 Open With Preview 在预览中打开该图片。当然还有一种方法能做到,在控制台的左侧,当断点执行时,你会看到相关对象,选择你要查看的对象,现在我也要查看UIImage对象,然后按space键,也能将图片显示出来。
Image2 刚刚上面说的两种方法,除了能显示UIImage对象以后,UIImageView、UIView也行,至于其他的类型,我以前没有操作过,在以后的实践中可以试试。 po 的一个隐藏指令在lldb中,po (print object的缩写) 是打印某个对象的指令,自然的 po xxxx (某个对象)应该是我用的最多的指令了,其实还有一个指令用来查看某个View的层级结构关系 po [self.view recursiveDescription]。或许你觉得在控制栏下输出一大串代码不是很直观,那么你可以试试Xcode6 出来的 View debug方法,它能很好的解决这个问题,能更直观的查看视图的层级结构。 改变某些值有的时候,我们程序运行时改变某个值,来看看效果。我们可以用exp(expression的缩写)。 改变某个字符串的值? - (lldb) expression NSString *$xxString = @"111"
- G: L) P' o1 {# v - (lldb) po $xxString
0 T% s2 ~( i2 N, c5 g! [# L - 111: e! O6 g# Z6 n' w+ }
6 C3 n( i- j P- (lldb) po $xxString = @"2222"0 k5 V2 t3 Y+ f% D* \
- 2222% D9 y1 f9 |1 R; b' t2 j5 M) D
: R1 r( V# T/ m B- (lldb) expression $xxString = @"3333"
1 d7 R6 t; j- q# Q - (__NSCFString *) $2 = 0x00007fc793675a10 @"3333"
2 K; J4 N/ h& O k3 W$ C/ i- d) \3 T - (lldb) po $xxString
: U( F$ O& w1 [0 h9 ~ - 3333
( Y& p6 n# H% _* c: V9 x - 2 D1 r( G n2 D& v% t* i" D
- (lldb)
复制代码哦哦,对了 exp Class *$instance是在lldb中创建实例。 - (lldb) expression int $b = 108 |; Z4 x" B1 |6 p. u6 f1 _
- (lldb) po $b+ L6 c- p! D f( f, V: e
- 105 o* D3 a5 C9 K, n
5 D6 q" t ]2 {1 I1 j7 Z2 @* z- (lldb) exp $b = 100: N3 z; [' K% E+ f+ y
- (int) $0 = 100, Q5 I J6 C! W
- (lldb) po $b( R! h$ F' F n9 D
- 1003 T- Q2 G8 m. O) J1 \! {
- ' n0 e+ }8 ~8 g1 J
- (lldb)
复制代码 当然,我们还可以改变某个对象的属性,比如,如果我想要改变一个视图的背景颜色。- po self.view.backgroundColor = [UIColor redColor], l% n' \& g' z* M, e. p* y: b6 `8 {
- call imageView.backgroundColor = [UIColor greenColor]
复制代码有时候,我想需要改变某个函数的返回值,来测试函数的稳健性,那可以试试 thread return XXX指令。 看一下下面的代码吧。 - /// Value
; f7 T# [$ x V7 {9 d) ]; Y* k. G - NSString *string = [self exeGetReturnString];- m1 z0 Z9 I+ {' X! O
- 1 ?% G3 Y% O- R7 {- t! E
- /// Method* ]6 f+ }# c2 C6 H% ~7 O
- - (NSString *)exeGetReturnString {
$ d/ t* S/ ^/ r+ l1 ~# U - * f# I6 |" g+ s/ k6 Z7 ^( S
- return @"1111";
- b1 H) Y: S, p - }" N0 ?) D3 }+ V2 I( e0 H' q
5 V. g7 L# t9 Z a- /// lldb
; W: x2 P4 `/ u& i9 s/ b) }' p( B2 I" } - (lldb) thread return @"This is Changed String!!!"
( V6 m. E& \; R% c - (lldb) po string4 g7 x; f6 a! _
- This is Changed String!!!
& ~4 s7 J2 N5 t - % e! B: A" V/ ?* }" b9 A. a. v& V
- (lldb)
复制代码我把断点设置在函数exeGetReturnString的返回值前面,可以看出string的值变了。这些改变值的方法,有时在调试程序的时候是非常有用的。 指定输出的格式
对于基本简单类型,我们可以指定它的输出类型p/x(x表示输出的类型),这种最能体现在的就是整数类型数据之间二进制、八进制、十进制、十六进制的转换,虽然我们能算出来,但电脑应该算的比我们快,比我们准确吧!好吧,那让我们来一发试试吧。 - (lldb) expression int $a = 1
" ^5 }4 v9 D" Z. D/ f9 H - (lldb) po $a
- {1 r5 @5 h. ]# N: n8 m - 1" i$ I9 F) a: e1 b4 s5 Z
1 V8 f, U2 \; z' C- (lldb) p/t $a% `/ Y" D# f) p7 L6 Q$ R8 r! y% `3 q
- (int) $a = 0b000000000000000000000000000000019 }' b1 e+ o* B2 E) U; x9 R, Z
- (lldb) p/o $a* R [% u* z& q
- (int) $a = 01' K. X; p: m% A6 G; Z0 X
- (lldb) p/x $a. d; J7 U6 \( f0 t+ x' {+ K( g8 D
- (int) $a = 0x00000001
1 o( G6 C3 I3 i0 ?7 t+ W - (lldb) p/d 0b00000000000000000000000000000001! E5 _: L/ P* x2 J/ r5 K1 X
- (int) $0 = 1' K" C' y( G$ ~0 _( m! ]5 j; N! Q
- (lldb) p/d 01
4 A: S1 H& o% A* _8 _ - (int) $1 = 1
- d+ \2 v3 r: z# @- e - (lldb) p/d 0x00000001
4 c! c7 S# Q( t4 Q! R) w4 M - (int) $2 = 11 v1 Z, k$ G5 Y
- (lldb)
复制代码
/ |: e5 } \- O- }7 l4 S$ c9 h! s% l上面用的p/X(X代表进制,t,二进制;o,八进制;d,十进制;x,十六进制)。 上面输出的$X(0-2),这是啥意思呢?其实可以将他们看作是对操作对象的一个引用,可以直接使用这个符号来操作相对应的对象。 - (lldb) expression int $a = 10 o+ y3 Y% R6 } D
- (lldb) po $a
P7 ]5 ~8 V% Z1 ^, f G - 1- [# v. C9 Z2 Z
- ! q, ]9 m3 X- {% P
- (lldb) p/t $a& `. C9 `# t* B1 ?+ o( X/ j
- (int) $a = 0b00000000000000000000000000000001
4 _+ `, m" D) U" R4 e. F! z( e- V - (lldb) p/o $a) T* p% |. _& C. ^ T
- (int) $a = 01
" F) m- d' {, s* T1 P - (lldb) p/x $a0 X5 K Z8 I7 W' e. F" R
- (int) $a = 0x000000015 G0 G. C5 F1 K4 y- _9 a
- (lldb) p/d 0b00000000000000000000000000000001
% b$ [% w! m& g. h) }& r - (int) $0 = 1* h. t) n, G8 F; A; D
- (lldb) p/d 01
5 z3 V6 z6 f o7 e$ T# ` U - (int) $1 = 1* k( P% v% A6 c: Z- X, P
- (lldb) p/d 0x000000016 y4 t2 n( m- ^/ X! q2 g
- (int) $2 = 19 _6 @' z3 T& e2 u
- (lldb)
复制代码 查找奔溃信息的位置
程序崩溃了要咋办?找到崩溃的地方,解决它撒。我们可以添加一个全局的断点Add Exception Breakpoint,当程序崩溃的时候,它能定位错误的代码位置。但是还有一种方法能找到错误的位置。image lookup --address 0xXXXXXXXX(0xXXXXXXXX为程序奔溃时控制台给出的地址)。 - NSString *originalString = @"0123";
! z" h* n; N* W! _0 w7 L - NSString *subString = [originalString substringFromIndex:5];
复制代码好吧,这个代码的问题比较简单,我只是举个例子哈,[emoji]。 - 2015-05-17 23:26:04.790 DebugMethod[5282:1628084] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSCFConstantString substringFromIndex:]: Index 5 out of bounds; string length 4'! Y! E% d" y& r: }: H5 b Q0 q7 C$ q
- *** First throw call stack:; I n! h8 W7 M1 Z- X- {
- (1 m* `4 e" i" `, L
- 0 CoreFoundation 0x0000000104a70c65 __exceptionPreprocess + 165
- e7 f# }9 [( R/ O6 E - 1 libobjc.A.dylib 0x0000000104707bb7 objc_exception_throw + 45& }( H6 Z( g/ P- l
- 2 CoreFoundation 0x0000000104a70b9d +[NSException raise:format:] + 205
4 M. h3 \$ z# l& m" V, S - 3 Foundation 0x0000000104276ca8 -[NSString substringFromIndex:] + 118
, i" T7 t' r' b! E! ^) h ~+ } - 4 DebugMethod 0x00000001041d3936 -[ViewController viewDidLoad] + 7103 w2 ?8 k! I) Q+ t( x7 J
- 5 UIKit 0x0000000104f9b210 -[UIViewController loadViewIfRequired] + 738
5 R( b5 H+ o: Z" J4 d+ W - 6 UIKit 0x0000000104f9b40e -[UIViewController view] + 27; e& _1 f+ T r' b& p; y/ d
- 7 UIKit 0x0000000104eb62c9 -[UIWindow addRootViewControllerViewIfPossible] + 58
* _$ l# o9 j6 A+ q - 8 UIKit 0x0000000104eb668f -[UIWindow _setHidden:forced:] + 247
/ O; y- t6 |/ H( E' D - 9 UIKit 0x0000000104ec2e21 -[UIWindow makeKeyAndVisible] + 42
; \* _" o. M5 D+ V- `; ^ - 10 UIKit 0x0000000104e66457 -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 2732" U0 t% }: z+ b5 Y$ l) P
- 11 UIKit 0x0000000104e691de -[UIApplication _runWithMainScene:transitionContext:completion:] + 1349' k3 z8 H& ]3 k5 o/ u
- 12 UIKit 0x0000000104e680d5 -[UIApplication workspaceDidEndTransaction:] + 179% N- j" G7 T3 [$ e2 I4 H" ~* L
- 13 FrontBoardServices 0x000000010766f5e5 __31-[FBSSerialQueue performAsync:]_block_invoke_2 + 21" a9 P4 W. V( o: |
- 14 CoreFoundation 0x00000001049a441c __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 126 i3 W( S5 B. O f, J* q
- 15 CoreFoundation 0x000000010499a165 __CFRunLoopDoBlocks + 341) F1 @" h- Y' t
- 16 CoreFoundation 0x0000000104999f25 __CFRunLoopRun + 2389
3 a* r0 D( S* o; ~2 I - 17 CoreFoundation 0x0000000104999366 CFRunLoopRunSpecific + 470/ ~$ q* }4 @/ A% ?$ H3 W% E q
- 18 UIKit 0x0000000104e67b42 -[UIApplication _run] + 413
" F( s& V* n( j2 x# ` - 19 UIKit 0x0000000104e6a900 UIApplicationMain + 1282- U9 W. {- u" T" R4 Y5 g
- 20 DebugMethod 0x00000001041d3d3f main + 1112 c4 z+ i3 R" d `
- 21 libdyld.dylib 0x000000010703f145 start + 1- T1 p( n- [6 T( m* K; X
- )
2 U; z0 l1 n4 F* O) v - libc++abi.dylib: terminating with uncaught exception of type NSException
复制代码其实从上面的崩溃信息中,我们可以看出是越界NSRangeException的问题,原因是'*** -[__NSCFConstantString substringFromIndex:]: Index 5 out of bounds; string length 4',字符串本来长度为4,而要取的位置为5,所以越界了!!! 假如,如果你一个ViewController里面用到了多个[NSString substringFromIndex:]方法,那是不是比较难找到崩溃的地方?这样image lookup --address 0xXXXXXXXX就派上用场了,从上面的错误信息中,可以初步断定viewDidLoad方法里的substringFromIndex:方法出错了,那我们就拿viewDidLoad的里面的错误地址取寻找。 - (lldb) image lookup --address 0x00000001041d3936
: y7 s |% B; w! N. E - Address: DebugMethod[0x0000000100001936] (DebugMethod.__TEXT.__text + 710)! Y" s- `( Z8 u/ m2 {( C& {
- Summary: DebugMethod`-[ViewController viewDidLoad] + 710 at ViewController.m:34
4 Q3 O, H+ b9 B+ |3 W7 R/ u1 x - (lldb)
复制代码结果还真的被猜中了,从上面的结果来看,代码错在ViewController.m文件viewDidLoad34行。 一些问题的处理
在使用以上一些方法的时候,也遇到了一些问题。有的时候,它找不到对象类型或者方法,比如下面这个: - (lldb) po self.view.frame
' ^! f) C* |, V5 N5 T - error: property 'frame' not found on object of type 'UIView *'. A9 }. w `0 a
- error: 1 errors parsing expression# s* @5 u' ?3 A: }
- (lldb) po (CGRect)self.view.frame
* v; p$ R: j6 S- B) ` - error: 'CGRect' is not a valid command.3 s5 e, ~! T* i: X X4 _1 I; @
- (lldb) po (CGRect)[self.view frame]( C! N( [& c& e. X
- (origin = (x = 0, y = 0), size = (width = 375, height = 667))3 h' v6 F3 o: j) y3 b! @; J
- (origin = (x = 0, y = 0), size = (width = 375, height = 667))
1 R4 y$ z D8 S8 j* D+ J - (lldb) exp @import UIKit$ A* c7 A- t/ p: i* z: u
- (lldb) po self.view.frame
% I9 k' T: @0 f; [. e - (origin = (x = 0, y = 0), size = (width = 375, height = 667))
3 T! v. q5 ?% ? - (origin = (x = 0, y = 0), size = (width = 375, height = 667))
2 o2 y6 b* c9 v7 P+ x8 |. b# R - (lldb)
复制代码这个是找不到UIView的frame属性,我们可以制定输出的类型,或者引入UIKit库。 - (lldb) expr NSDictionary *$tmpDict = [dataDict[@"Data"] firstObject]
, K% x& q3 v5 g5 u - error: no known method '-firstObject'; cast the message send to the method's return type
s7 _$ ~9 S3 S6 H. }: r o0 { - error: 1 errors parsing expression
. `9 ^. J/ u1 ^! k! E - (lldb) expr NSDictionary *$tmpDict = (NSDictionary *)[dataDict[@"Data"] firstObject]4 H0 l1 q: @" u
- (lldb) po $tmpDict
: R% w" t( z9 e6 }: D% `+ g9 X - {
( ]" \8 o0 W% Y3 K7 I: S( ] - Key = 1;
0 h) @) s( R8 t0 K( W( T - }
复制代码这个是找不到-firstObject这个方法。 总结
以上这些我提到的方法,只是调试中的冰山一角,lldb里还有很多宝藏去挖掘。在开发项目中,对我们开发有利的去多了解,至于其他更牛逼的技巧(与实际开发项目没有多大关系的),我们有时间、有精力、有想法的可以多去探讨。研究的越多、越广,你会发现自己有很多很多很多很多都不知道,[emoji],发现自己很菜很菜,因为搞技术就像一个无底洞嘛。 参考 原文地址:http://joakimliu.github.io/2015/05/22/Some-Debug-Method-in-iOS/ |