LLDB 是一个有着 REPL 的特性和 C++ ,Python 插件的开源调试器。LLDB 绑定在 Xcode 内部,存在于主窗口底部的控制台中。调试器允许你在程序运行的特定时暂停它,你可以查看变量的值,执行自定的指令,并且按照你所认为合适的步骤来操作程序的进展。(这里有一个关于调试器如何工作的总体的解释。)
相信每个人或多或少都在用LLDB来调试,比如po
一个对象。LLDB的是非常强大的,且有内建的,完整的 Python 支持。今天我们主要介绍一个 facebook 开源的 lldb 插件 Chisel。可以让你的调试更Easy.
1.安装Chisel
源码地址: Chisel
Chisel 使用 homebrew 来安装,如果你没有安装homebrew, 参考 homebrew。
1 2 | |
安装完成按照安装日志上的提示,在~/.lldbinit
文件中添加一行,没有则新建。 提示类似如下:
1 2 3 | |
做好上面的步骤,然后重启Xcode就可以尝试下了。
注:使用命令行“ls -a
”查看是否有“.lldbinit”的文件,如果没有该文件,我们可以利用“touch ~/.lldbinit”创建该文件。然后将如下图中的地址复制到文件中,保存退出。重启Xcode,暂停后在控制台输入“help”,如果出现如图二的显示则表示安装成功。(“brew uninstall”可以卸载已安装的chisel)
图一
图二
2.内置命令
Chisel 为lldb提供了新增的便捷命令,是非常实用的命令
2.1 pviews
这个命令可以递归打印所有的view,并能标示层级,相当于 UIView 的私有辅助方法 [view recursiveDescription]
。 善用使用这个功能会让你在调试定位问题时省去很多麻烦。
使用示例:
1 2 3 4 5 6 7 8 9 | |
2.2 pvc
这个命令也是递归打印层级,但是不是view,而是viewController。利用它我们可以对viewController的结构一目了然。 其实苹果在IOS8也默默的添加了 UIViewController 的一个私有辅助方法[UIViewController _printHierarchy]
同样的效果。(注:配合符号断点,也可以定位ViewController。在符号断点中输入“viewDidLoad”,便会在所有初始化ViewController时都会断一下。cmd+shift+o是快速定位)
预览效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
是不是方便很多呢,而且还可以看到 viewController 是否已经 viewDidLoad .
2.3 visualize
这是个很有意思的功能,它可以让你使用Mac的预览打开一个 UIImage, CGImageRef, UIView, 或 CALayer。 这个功能或许可以帮我们用来截图、用来定位一个view的具体内容。 但是在我试用了一下,发现暂时还是只能在模拟器时使用,真机还不行。(注:visualize可以用在已打印出的对象的地址(地址也可以通过viewdebuger获取)预览该对象,例如“visualize0x7f82617107f0”)
使用简单:
1
| |
2.4 fv & fvc
fv
和 fvc
这两个命令是用来通过类名搜索当前内存中存在的view和viewController实例的命令,支持正则搜索。
如:
1 2 3 4 5 6 7 8 9 | |
2.5 show & hide
这两个命令用来显示和隐藏一个指定的 UIView . 你甚至不需要Continue Progress. 就可以看到效果。
2.6 mask/umask border/unborder
这两组命令用来标识一个view或layer的位置时用, mask用来在view上覆盖一个半透明的矩形, border可以给view添加边框。但是在我实际使用的过程中mask总是会报错,估计是有bug, 那么mask/unmask 一般不要用好了,用border命令是一样的效果,反正二者的用途都是找到一个对应的view.
2.7 caflush
这个命令会重新渲染,即可以重新绘制界面, 相当于执行了 [CATransaction flush]
方法,要注意如果在动画过程中执行这个命令,就直接渲染出动画结束的效果。
当你想在调试界面颜色、坐标之类的时候,可以直接在控制台修改属性,然后caflush
就可以看到效果啦,是不是要比改代码,然后重新build省事多了呢。
例, 其中 $122 即是目标UIView:
1 2 3 4 | |
2.8 bmessage
这个命令就是用来打断点用的了,虽然大家断点可能都喜欢在图形界面里面打,但是考虑一种情况:我们想在 [MyViewController viewWillAppear:]
里面打断点,但是 MyViewController并没有实现viewWillAppear:
方法, 以往的作法可能就是在子类中实现下viewWillAppear:
,然后打断点,然后rebuild。
那么幸好有了 bmessage
命令。我们可以不用这样就可以打这个效果的断点: (lldb) bmessage -[MyViewController viewWillAppear:]
上面命令会在其父类的viewWillAppear:
方法中打断点,并添加上了条件:[self isKindOfClass:[MyViewController class]]
2.9 border
这个命令是用来给某个对象加边框,方便与其他的视图对象有所区别。
border -c red -w 2 0x7f82617107f0(对象地址),此时模拟器会自动刷新界面并对该对象加上边框宽度为2的红色边框。
unborder 0x7f82617107f0,这条命令是去掉刚才加的边框属性
。
2.10 caflush补充
这个命令是在你做了修改view的外观之后,可以让屏幕立刻刷新成你修改的外观样式。因为在一些chisel命令中是默认刷新的,但是有些命令是需要我们自己去手动刷新的。
在控制台输入“po [
setBackgroundColor:[UIColor redColor]]”后,需要在输入“caflush”模拟器的界面才会有变化。但是不建议我们去手动改变对象的背景色,因为我们必须去改过来或者重新启动工程才能还原。通常建议是加边框来区别。0x7f82617107f0
2.11 presponder
这个命令是输出对象的响应者链。presponder
0x7f82617107f0
,在控制器中从APPDelegate到当前对象的响应者链,不会显示同级的对象,方便我们梳理一些层级关系和响应事件的执行。
2.12 taplog
这个命令是在你程序中断时,你敲击模拟器屏幕的任何地方,控制器会打印到你触摸的view。方便我们快速查找获取触摸的view地址,以便利用其它的命令来做其它的操作。注意:我们需要 先输入“taplog”,再去点击屏幕。2.13 pcalss
这个命令可以打印当前对象的类的继承关系。
pclass 0x7f82617107f0
结果:
UITableViewLabel
| UILabel
| | UIView
| | | UIResponder
| | | | NSObject
2.14 pinternals
这个命令是打印对象的继承关系、对象属性、实例方法等详细信息,其中包括我们自定义的属性和方法。
结果:(UITableViewCell) $5 = {
UIView = {
UIResponder = {
NSObject = {
isa = UITableViewCell
}
_hasOverrideClient = false
_hasOverrideHost = false
_hasInputAssistantItem = false
}
_constraintsExceptingSubviewAutoresizingConstraints = nil
_cachedTraitCollection = 0x00006000000dc9a0
_layer = 0x0000608000029be0
_layerRetained = nil
_gestureInfo = nil
_gestureRecognizers = nil
_window = 0x00007fde66006a90
_subviewCache = 0x000060000005c920 @"2 elements"
_templateLayoutView = nil
_charge = 0
_tag = 0
_viewDelegate = nil
_backgroundColorSystemColorName = 0x000060800005c110 @"tableCellPlainBackgroundColor"
_countOfMotionEffectsInSubtree = 0
_countOfTraitChangeRespondersInDirectSubtree = 3
_cachedScreenScale = 3
_layoutSubviewsCount = 0
_retainCount = 4
_tintAdjustmentDimmingCount = 0
_shouldArchiveUIAppearanceTags = false
_wantsDeepColorDrawing = true
_interactionTintColor = nil
_layoutMarginsGuide = nil
_minXVariable = nil
_minYVariable = nil
_boundsWidthVariable = nil
_boundsHeightVariable = nil
_layoutEngine = nil
_layoutDebuggingIdentifier = nil
_stashedLayoutVariableObservations = nil
_internalConstraints = nil
_continuousCornerRadius = 0
_countOfFocusedAncestorTrackingViewsInSubtree = 0
_semanticContentAttribute = 0
_contentSizeNotificationToken = 0x000060800005c2f0
_readableContentGuide = nil
__preferedContentsFormat = 0
_previewingSegueTemplateStorage = nil
__presentationControllerToNotifyOnLayoutSubviews = nil
}
_tableView = nil
_layoutManager = 0x0000608000002520
_target = nil
_editAction = <no value available>
_accessoryAction = <no value available>
_oldEditingData = nil
_editingData = nil
_rightMargin = 0
_indentationLevel = 0
_indentationWidth = 10
_reuseIdentifier = 0x0000000107074b10 @"Cell"
_floatingContentView = nil
_lineBreakModeBeforeFocus = 0
_contentView = 0x00007fde66002050
_imageView = nil
_textLabel = 0x00007fde6600e3e0
_detailTextLabel = nil
_backgroundView = nil
_selectedBackgroundView = nil
_multipleSelectionBackgroundView = nil
_selectedOverlayView = nil
_selectionFadeDuration = 0.5
_backgroundColor = 0x000060800007d5c0
_separatorColor = 0x0000600000077640
_separatorEffect = nil
_topShadowColor = 0x000060800005c170
_bottomShadowColor = 0x000060800005c1a0
_sectionBorderColor = 0x0000600000077640
_floatingSeparatorView = nil
_topShadowAnimationView = nil
_bottomShadowAnimationView = nil
_badge = nil
_unhighlightedStates = 0x0000000000000000
_highlightingSupport = nil
_selectionSegueTemplate = nil
_accessoryActionSegueTemplate = nil
_accessoryActionPreviewingSegueTemplateStorage = nil
_accessoryView = nil
_editingAccessoryView = nil
_customAccessoryView = nil
_customEditingAccessoryView = nil
_separatorView = 0x00007fde63f089a0
_topSeparatorView = nil
_topShadowView = nil
_editableTextField = nil
_lastSelectionTime = 0
_deselectTimer = nil
_textFieldOffset = 114
_indexBarWidth = 0
_returnAction = <no value available>
_selectionTintColor = 0x0000608000055cf0
_accessoryTintColor = nil
_reorderControlImage = nil
_menuGesture = 0x00007fde6600e070
_representedIndexPath = nil
_focusable = false
_swipeToDeleteConfirmationView = nil
_swipeToDeleteCancelationGesture = nil
_clearBlendingView = nil
_swipeToDeleteDistancePulled = 0
_sectionCornerRadius = 0
_sectionBorderWidth = 0
_defaultMarginWidth = 20
_editControlFocusGuide = nil
_reorderControlFocusGuide = nil
_constants = 0x0000600000002410
_isLayoutEngineSuspended = false
}
3. 自定义命令
我们也可以自定义插件,不过前提是要懂一些 python。 比如设计一个打印keyWindow的windowLevel的命令:
创建python脚本文件 /magical/commands/example.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
其中定义了PrintKeyWindowLevel
的类,需要实现 name
description
run
方法来分别告诉名称、描述、和执行实体。
创建好脚本后,然后在前面安装时创建的 ~/.lldbinit
文件中添加一行:
1
| |
然后重启Xcode之后就可以使用自定义的命令啦。
参考文献: