修复 bug 占用我们日常开发的大部分时间,熟练的使用调试工具可以给我们节约大部分的时间。

LLDB 的常用命令

expression

expresion 是一个非常常用的命令,我们可以通过这个命令来执行一些表达式,这样我们就不需要重写运行工程了,例如:

  (lldb) expression -- self.view.backgroundColor = [UIColor greenColor]
(lldb) expression -- (void)[CATransaction flush] // 用于刷新页面,

我们也可以使用 expression 来输出我们关注的信息

(lldb) expression -O -- self.view //这时候 就会输出对象 UIView,注意 -O 代表输出的是一个对象 ,这里有另外一种简写方式 po.
<UIView: 0x7d2974a0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7d294813>>

有时候我们自定义的类并没有重写 description 方法,如果我们直接输出这个对象的话可能只会显示这个类名和地址,例如

(lldb) expression -O -- self
<JChatAboutMeViewController: 0x7d2974a0>

这地址并不是我们想要的,我们想要的是这个对象内部信息,这里我推荐一个插件 chisel ,我们可以使用chisel 提供的命令来打印这个对象, 例如

(lldb) pinternals 0x7d2974a0
(JChatAboutMeViewController) $4 = {
UIViewController = {
UIResponder = {
NSObject = {
isa = JChatAboutMeViewController
}
_hasOverrideClient = '\0'
_hasOverrideHost = '\0'
_hasInputAssistantItem = '\0'
}
_overrideTransitioningDelegate = nil
_view = nil
_tabBarItem = nil
_navigationItem = 0x7d4abf40
_toolbarItems = nil
_title = nil
_nibName = nil
_nibBundle = nil
_parentViewController = 0x7cb84c00
_childModalViewController = nil
_parentModalViewController = nil
_previousRootViewController = nil
_modalTransitionView = nil
_modalPreservedFirstResponder = nil
_dimmingView = nil
_dropShadowView = nil
_currentAction = nil
_storyboard = nil
_externalObjectsTableForViewLoading = nil
_topLevelObjectsToKeepAliveFromStoryboard = nil
_savedHeaderSuperview = nil
_savedFooterSuperview = nil
_editButtonItem = nil
_searchDisplayController = nil
_strongSearchDisplayController = nil
_modalTransitionStyle = 0
_modalPresentationStyle = 0
_lastKnownInterfaceOrientation = 0
_popoverController = nil
_containerViewInSheet = nil
_recordedContentScrollView = nil
_afterAppearance = nil
_explicitAppearanceTransitionLevel = 0
_interfaceBuilderKeyCommands = nil
_addedKeyCommands = nil
_overrideTraitCollections = nil
_previewSourceViews = nil
_retainCount = 0
_ignoreAppSupportedOrientations = '\0'
_viewHostsLayoutEngine = '\0'
_storyboardIdentifier = nil
_transitioningDelegate = nil
_frozenTraitCollection = nil
overrideUseCustomPresentation = '\0'
_modalPresentationCapturesStatusBarAppearance = '\0'
_disablesAutomaticKeyboardDismissal = '\0'
_ignoresParentMargins = '\0'
_childViewControllers = nil
_customNavigationInteractiveTransitionDuration = 0
_customNavigationInteractiveTransitionPercentComplete = 0
_customTransitioningView = nil
_lastNotifiedTraitCollection = nil
_presentationController = nil
_preferredFocusedItem = nil
_navigationControllerContentOffsetAdjustment = 0
_contentMargin = 16
_topLayoutGuide = nil
_bottomLayoutGuide = nil
_topBarInsetGuideConstraint = nil
_bottomBarInsetGuideConstraint = nil
_storyboardSegueTemplates = nil
_segueResponsibleForModalPresentation = nil
_sourceViewControllerIfPresentedViaPopoverSegue = nil
_modalSourceViewController = nil
_presentedStatusBarViewController = nil
_edgesForExtendedLayout = 15
__childControllerToIgnoreWhileLookingForTransitionCoordinator = nil
_presentingFocusedItem = nil
_storyboardPreviewSegueTemplates = nil
_storyboardCommitSegueTemplates = nil
_storyboardPreviewingRegistrants = nil
__embeddedView = 0xffffffff
__embeddingView = 0x78b909c0
__embeddedDelegate = 0x00007faa
_originalPresentationController = 0x78af3630
_temporaryPresentationController = 0x00007faa
}
}

这样我们就能看到这个对象的内部信息了。

chisel 还提供一些便捷的功能,比如打印 pviews 递归打印层级,不过我更喜欢使用 xcode 自带的debug view hierarchy,这样更加直观。

我们可以使用 pvc 递归输出试图控制器的层级关系,例如

(lldb) pvc
<JChatSwift.JChatMainTabViewController 0x7d49d560>, state: appeared, view: <UILayoutContainerView 0x7d479860>
| <UINavigationController 0x7c3afc00>, state: appeared, view: <UILayoutContainerView 0x7be8e9a0>
| | <JChatConversationListViewController 0x7d49d2b0>, state: disappeared, view: <UIView 0x7d375320> not in the window
| | <JChatChattingViewController 0x7bfbdde0>, state: appeared, view: <UIView 0x7d373b30>
| <UINavigationController 0x7eb7a000>, state: disappeared, view: <UILayoutContainerView 0x7d4a2c20> not in the window
| | <JChatContactsViewController 0x7bea3d70>, state: disappeared, view: <UILayoutContainerView 0x7d4a1d40> not in the window
| <UINavigationController 0x7cb84c00>, state: disappeared, view: <UILayoutContainerView 0x7bf87590> not in the window
| | <JChatAboutMeViewController 0x7d2974a0>, state: disappeared, view: (view not loaded)

通过 pvc 和 pinternals 这样我们就可以在任何地方了解我们所有界面状态了。

在 xcode8 以后,我们也可以通过debug memory graph 来查看程序运行的内存状态。

thread

我们可以使用 thread backtrace 来输出线程的堆栈信息,例如

(lldb) thread backtrace  // 这个命令可以简写为bt
* thread #1: tid = 0x14169a, 0x001273e5 JChatSwift`JChatChattingViewController.viewDidLayoutSubviews(self=0x7b6bc560) -> () + 21 at JChatChattingViewController.swift:48, queue = 'com.apple.main-thread', stop reason = breakpoint 7.1
* frame #0: 0x001273e5 JChatSwift`JChatChattingViewController.viewDidLayoutSubviews(self=0x7b6bc560) -> () + 21 at JChatChattingViewController.swift:48
frame #1: 0x00127502 JChatSwift`@objc JChatChattingViewController.viewDidLayoutSubviews() -> () + 34 at JChatChattingViewController.swift:0
frame #2: 0x039dc811 UIKit`-[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1598
frame #3: 0x0305c1b9 libobjc.A.dylib`-[NSObject performSelector:withObject:] + 59
frame #4: 0x03791769 QuartzCore`-[CALayer layoutSublayers] + 141
frame #5: 0x03784a47 QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 401
frame #6: 0x0378489d QuartzCore`CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 21
frame #7: 0x0370e49f QuartzCore`CA::Context::commit_transaction(CA::Transaction*) + 339
frame #8: 0x0373d290 QuartzCore`CA::Transaction::commit() + 498
frame #9: 0x0373eda0 QuartzCore`CA::Transaction::flush_transaction() + 38
frame #10: 0x0393685c UIKit`_afterCACommitHandler + 375
frame #11: 0x022f676e CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 30
frame #12: 0x022f66c7 CoreFoundation`__CFRunLoopDoObservers + 391
frame #13: 0x022da3a6 CoreFoundation`__CFRunLoopRun + 1414
frame #14: 0x022d9bab CoreFoundation`CFRunLoopRunSpecific + 395
frame #15: 0x022d9a0b CoreFoundation`CFRunLoopRunInMode + 123
frame #16: 0x06a1ab4c GraphicsServices`GSEventRunModal + 177
frame #17: 0x06a1a9c7 GraphicsServices`GSEventRun + 80
frame #18: 0x039077fb UIKit`UIApplicationMain + 148
frame #19: 0x001a7ce1 JChatSwift`main + 145 at AppDelegate.swift:15
frame #20: 0x06037799 libdyld.dylib`start + 1

我们可以看到程序停在JChatChattingViewController.swift:48 这一行

watchpoint

监视某个变量的改变,有时候我们想知道一个对象在什么时候被修改了,我们可以使用 watchpoint set var,当var 改变的时候程序就就会停在改变的地方, 如果用 chisel

的话,我们可以使用 wivar 命令来监听值的变化,例如

(lldb) wivar self name

作者:HuminiOS - 极光

原文:iOS 调试篇

知乎专栏:极光日报

iOS 调试心得的更多相关文章

  1. LPC2478_调试心得(转)

    1.在调试“E:\htwang\smart2200v201\ARM嵌入式系统实验教程(二)\开发板出厂编程程序\液晶显示程序\LCM_Disp”的程序时,想使用外部RAM进行仿真调试,在将ADS1.2 ...

  2. iOS调试

    iOS高效调试 写代码难免出现bug.储备些调试技能绝对能够提高你的工作效率,让bug无所遁形.下面就和大家分享一些我在工作中常用的iOS调试小技能. 1. 打印 最简单,基础的调试方法就是打印日志了 ...

  3. Xamarin.iOS调试提示需要iOS SDK

    Xamarin.iOS调试提示需要iOS SDK   错误信息:The version of Xamarin.iOS requires th iOS 9.3 SDK (shipped with Xco ...

  4. iOS 调试 crash breakpoint EXC_BAD_ACCESS SIGABRT

    原文地址:iOS 调试 crash breakpoint EXC_BAD_ACCESS SIGABRT作者:流年若离殇 在调试程序的时候,总是碰到crash的bug,而且一追踪就是一些汇编的代码,让人 ...

  5. MIPI接口LCD屏调试心得(转)

    源: MIPI接口LCD屏调试心得

  6. 提高Android和iOS调试编译速度

    http://www.cnblogs.com/findumars/p/7841252.html 提高Android和iOS调试编译速度   如果您使用Delphi开发App,就会遇到:Android和 ...

  7. uniapp - 关于ios调试

    [ios调试] 1.一台windows电脑.一根apple数据线(一旦连接以后,apple设备就会自动识别itunes软件,如果没有安装会提示) 2.安装itunes (爱思助手) 3.官方教程:ht ...

  8. VGA调试心得

    以前自己调试过视频信号,无非就时钟加行场同步加数据线,如果视频信号出问题,第一看现象,第二测频率,反正出问题不是消隐信号出问题,就是时钟频率出问题.通过这种方式也调试成功过几个显示屏,然后就以为自己对 ...

  9. React Native调试心得

    在做React Native开发时,少不了的需要对React Native程序进行调试.调试程序是每一位开发者的基本功,高效的调试不仅能提高开发效率,也能降低Bug率.本文将向大家分享React Na ...

随机推荐

  1. JVM内存划分基础知识

    第一部分 JVM内存划分 目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbag ...

  2. Oracle 11gR2 RAC ohasd failed to start 解决方法

    rcrCRS-4124: Oracle High Availability Services startup failed. CRS-4000: Command Start failed, or co ...

  3. 负载均衡软件LVS分析四(测试)

    一.启动LVS集群服务LVS负载均衡管理和使用有两种方式,一种是以ipvsadm命令行脚步与ldirectord监控方式,一种是以Piranha工具进行管理和使用.下面分别介绍. 1.利用ipvsad ...

  4. loadrunner:参数类型及其取值机制

    参数类型 参数名随意取,建议取通俗易懂的名字,下面我们重点介绍一下参数的类型. ●DateTime: 很简单, 在需要输入日期/时间的地方, 可以用DateTime 类型来替代. 其属性设置也很简单, ...

  5. node源码详解(三)—— js代码在node中的位置,process、require、module、exports的由来

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource3 本博客同步在https://cnodejs.o ...

  6. 2017《JAVA技术预备作业》 1502 陈明宇

    1.阅读邹欣老师的博客,谈谈你期望的师生关系是什么样的? 我期望的师生关系应该是亦师亦友的关系,美丽的校园是我们学生居住生活最久的地方而老师则是和我们接触最为密切的人.在课堂上,老师是辛勤的园丁,向我 ...

  7. LPC4370使用学习:GPIO的引脚功能使用,和12864OLED模拟I2C驱动

    一: 手中有块LPC4370的开发板,因为便宜,所以引脚引出的不多,而且只有基本的底板资源驱动代码和例程. 看着手册和例程看了老半天,写程序写了半天,结果GPIO老是驱动不起来,因为引脚配置寄存器中有 ...

  8. display的none与block(判断登录界面的账号密码是否为空)

    判断登录界面的账号密码是否为空的时候又不想用alert显示就需要用display来隐藏alert啦(在设置时切忌要将隐藏的内容写在账号和密码的div中,否则会根据屏幕的分辨率不同而有所变化,这是本人教 ...

  9. Struts2的概述和入门

    忽如一夜春风来,千树万树梨花开 上节我们说到,JAVAEE的三层架构,即web层,service层,dao层.hibernate是应用在dao层的.而我们现在所学的Struts2是应用在web层.St ...

  10. Sqlserver 链接服务器和同义词

    在数据库的日常维护中,经常会遇到跨服务器的数据传输. 例如A服务器上的数据每天要从B服务器上去获取数据,然后插入到自己的服务器上.这种情况就要用到链接服务器了. 接下来,我就把我本机当作服务器A,17 ...