1.Static Analyzer(静态分析)
Static Analyzer是一个非常好的工具, 它可以帮助我们发现编译器警告不会提示的问题。
Static Analysis 优点:
1、使用操作容易。
2、能够在编码阶段,开发自行进行代码检查。早期发现代码隐患。
3、直接分析源代码来发现程序中的错误,而不需要实际运行。
4、自动检测objective-C程序中的BUG,发现内存泄露和其它问题。
5、内存问题发现越早,解决的代价就越小。
Static Analysis 可以对以下一些情况进行分析:
1 retain和release的正确使用
2 未使用的实例变量
3 未初始化的变量
4 除0
5 类型不兼容
6 无法到达的代码路径
7 引用空指针
8 缺少dealloc
Static Analysis 使用方法:
1.打开Xcide的“Product”菜单,选择Analyzer选项(或者使用热键command + shift +B)。
2.如图所示Static Analyzer会捕获任何应用内可能的问题然后以蓝色的警告展示出来。
3.我们也可以设置我们的应用在我们编译应用的时候自动开启Static Analyzer,打开项目project文件,设置“Run Static Analyzer”为YES,如下图:
建议使用场景:
1、建议在开发编码阶段使用,直接分析源代码来发现程序中的错误,而不需要实际运行,提前发现代码隐患。
2、测试在冒烟测试时可先进行扫描,错误太多代码修改量太大的情况下可暂不介入测试,以免造成返工。
1、添加一个特殊的断点
异常断点(Exception breakpoint)
符号断点(Symbolic breakpoint)
2、打印到控制台
使用NSLog打印字符串
使用NSLog打印对象(po)
带条件的打印
在循环里面打印一些东西
3、运行时设置断点
4、调试中播放声音
5、LLDB中有用的一些命令
打印帮助
打印调用栈
打印最基本的内容 (p)
打印对象(po)
打印表达式(expr)
打印线程中的一些东西
下面是非常有用的一些Xcode调试技术(使用断点和LLDB调试器)
1、添加一个特殊的断点
异常断点(Exception breakpoint)
如果添加了异常断点,当程序每次发生了异常,都会被中断。一般用来捕获未知异常。如下示例:
1. *** Terminating app due to uncaught exception ’NSRangeException’, reason:
2. ’-[__NSCFArray objectAtIndex:]: index (10) beyond bounds (3)
符号断点(Symbolic breakpoint)
符号断点可以中断某个函数的调用。
1. - [UIViewController viewDidLoad]
2. - [__NSCFArray objectAtIndex:]
2、打印到控制台
使用NSLog打印字符串
使用断点来替换NSLog代码(或者在运行时添加一个NSLog)——与代码写NSLog的效果相同。
使用NSLog打印对象(po)
1. NSLog(@"obj: %@", obj);
带条件的打印
例如:当aNumber大于10才打印出“str”的内容。
1. expr (void)NSLog(@"Ok, print a log: %@", str)"
在循环里面打印一些东西
例如,在循环中希望i大于5才开始打印。
1. for ( int i=0; i<10; i++ )
2. {
3. [self self]; // something
4. }
使用“ignore”值,并利用下面的代码进行打印:
1. expr (void)NSLog(@"Ok, print a log: %@", str)
3、运行时设置断点
在运行的时候,根据条件设置断点有时候非常有用。
1. breakpoint set -f APViewController.m -l 33
5、LLDB中有用的一些命令
当Xcode停留在某个断点时,我们可以通过控制台(console)与 lldb进行交互。
打印帮助
1. (lldb) help
打印调用栈(bt)
1. (lldb) bt
2. * thread #1: tid = 0x1c03, 0x00003146 Debug`-[APViewController callMe:andANumber:](self=0x07187e50, _cmd=0x000038b9, str=0x0715aa40, aNum=38) + 230 at APViewController.m:33, stop reason = breakpoint 3.1
3. frame #0: 0x00003146 Debug`-[APViewController callMe:andANumber:](self=0x07187e50, _cmd=0x000038b9, str=0x0715aa40, aNum=38) + 230 at APViewController.m:33
4. frame #1: 0x0000304a Debug`-[APViewController viewDidLoad](self=0x07187e50, _cmd=0x005c5a77) + 122 at APViewController.m:16
5. frame #2: 0x000f41c7 UIKit`-[UIViewController loadViewIfRequired] + 536
6. frame #3: 0x000f4232 UIKit`-[UIViewController view] + 33
7. frame #4: 0x000433d5 UIKit`-[UIWindow addRootViewControllerViewIfPossible] + 66
8. frame #5: 0x0004376f UIKit`-[UIWindow _setHidden:forced:] + 368
9. frame #6: 0x00043905 UIKit`-[UIWindow _orderFrontWithoutMakingKey] + 49
10. frame #7: 0x0004c917 UIKit`-[UIWindow makeKeyAndVisible] + 65
11. frame #8: 0x00002e1b Debug`-[APAppDelegate application:didFinishLaunchingWithOptions:](self=0x07560750, _cmd=0x005a9c21, application=0x0716a640, launchOptions=0x00000000) + 571 at APAppDelegate.m:28
12. frame #9: 0x00010157 UIKit`-[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 266
13. frame #10: 0x00010747 UIKit`-[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1248
14. frame #11: 0x0001194b UIKit`-[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 805
15. frame #12: 0x00022cb5 UIKit`-[UIApplication handleEvent:withNewEvent:] + 1022
16. frame #13: 0x00023beb UIKit`-[UIApplication sendEvent:] + 85
17. frame #14: 0x00015698 UIKit`_UIApplicationHandleEvent + 9874
18. frame #15: 0x01becdf9 GraphicsServices`_PurpleEventCallback + 339
19. frame #16: 0x01becad0 GraphicsServices`PurpleEventCallback + 46
20. frame #17: 0x01c06bf5 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 53
21. frame #18: 0x01c06962 CoreFoundation`__CFRunLoopDoSource1 + 146
22. frame #19: 0x01c37bb6 CoreFoundation`__CFRunLoopRun + 2118
23. frame #20: 0x01c36f44 CoreFoundation`CFRunLoopRunSpecific + 276
24. frame #21: 0x01c36e1b CoreFoundation`CFRunLoopRunInMode + 123
25. frame #22: 0x0001117a UIKit`-[UIApplication _run] + 774
26. frame #23: 0x00012ffc UIKit`UIApplicationMain + 1211
27. frame #24: 0x00002b22 Debug`main(argc=1, argv=0xbffff3a4) + 130 at main.m:16
28. frame #25: 0x00002a55 Debug`start + 53
29. (lldb)
打印最基本的内容 (p)
1. (lldb) print anInt
打印对象(po)
1. (lldb) po anObj
2. (lldb) po 0x0715aa40
打印表达式(expr)
1. (lldb) expr 5+2
2. (lldb) expr aString = @"aNewValue"
打印线程中的一些东西
1. (lldb) help frame
1.Enable NSZombieObjects(开启僵尸对象)
Enable NSZombieObjects可能是整个Xcode开发环境中最有用的调试技巧。这个技巧非常非常容易追踪到重复释放的问题。该技巧会以非常简洁的方式打印指出重复释放的类和该类的内存地址。
怎么开启僵尸对象呢?首先打开“Edit Scheme”(或者通过热键?<),然后选择Diagnostics选项卡,勾选Enable NSZombie Objects选项。
现在我们可以关掉ARC来测试重复释放的问题、异常和更容易产生的Crashs,但是即使开启ARC,重复释放和与内存相关的Crashs也经常发生。现在假设我们犯了一些错误导致了重复释放的发生来观察将要发生什么。
UIView* view =[[[UIView alloc] init] autorelease];
//...
//do somethingwith view...clearly forgetting that it has been autoreleased.
//
[viewrelease];
现在运行这段代码会发生重复释放的问题,程序将崩溃在主函数,会像下面这样:
Enable NSZombieObjects会让调试器看起来像这样:
这个例子看起来作用非常小,但是对于一个大工程通过Enable NSZombie Objects打印的信息是非常有用的。
2.对于所有异常添加Global BreakPoint(全局断点)
当你的应用异常或者崩溃的时候Xcode喜欢做的一件事情是直接跳到主函数,就像第一个调试技巧中用到的那个例子一样。如果异常或者崩溃能够停留在异常或者崩溃发生的地方那该是多好呀。幸运的是有一个方法可以做到。
Xcode有一个漂亮的功能叫做异常断点,允许我们在异常发生的时候在异常发生的地方放一个断点。你可以指定具体的异常也可以指定所有的异常。
怎样开启异常断点呢?工程切换到异常浏览窗口,点击下方左侧的“Add Breakpoint”按钮,然后选择“Add Exception Breakpoint”确保可以捕获所有异常。
现在异常发生时调试器不会直接跳转到主函数了,而是停留在了异常发生的地方。
这个断点给了我们一个调试异常非常好的开始的点,不用我们再去遍历代买去找异常发生的地方了。
3.Static Analyzer(静态分析)
Static Analyzer是一个非常好的工具去发现编译器警告不会提示的问题和一些个人的内错泄露和死存储(不会用到的赋了值的变量)错误。这个方法可能大大的提高内存使用和性能,以及提升应用的整体稳定性和代码质量。
怎么打开Static Analyzer?打开Xcide的“Product”菜单,选择Analyzer选项(或者使用热键? shift B)。正像你看到的下面的截图,Static Analyzer会捕获任何应用内的可能的问题然后以蓝色的警告展示出来。
我们也可以设置我们的应用在我们编译应用的时候自动开启Static Analyzer,打开项目project文件,设置“Run Static Analyzer”为YES,如下图:
4.CondationalBreakpoints(条件断点)
最后一个调试技巧条件断点,这只是普通的断点,当变量满足一定条件时程序停止。这个调试技巧在当你想要捕获一个循环中的变量的特定值或者一些不常发生的情况时是非常有用的,而不用你每次迭代都停止来查看。
怎样开启条件变量?只不过是添加一个普通断点,然后右键点击断点选择“Edit Breakpoint”,这时就打开了一个断点编辑器,你可以在这里设置断点条件(以及一些其他的断点设置),然后选择“Done”,这个调试技巧非常简单吧。
5.unrecognizedselector send to instancd快速定位
在Debug菜单中Breakpoints->Create Symbolic Breakpoint
在Symbolic中填写如下方法签名
-[NSObject(NSObject)doesNotRecognizeSelector:]
设置完成后再遇到类似的错误就会定位到具体的代码