chisel是facebook开源的LLDB插件,方便开发者在开发过程中提升开发效率,或是方便从新接手的一个旧项目中快速熟悉起来。
以下内容多来自博客:https://blog.cnbluebox.com/ 因为有些东西都是一样的,就直接拷过来了,有部分是我后来补充的。
LLDB 是一个有着 REPL 的特性和 C++ ,Python 插件的开源调试器。LLDB 绑定在 Xcode 内部,存在于主窗口底部的控制台中。调试器允许你在程序运行的特定时暂停它,你可以查看变量的值,执行自定的指令,并且按照你所认为合适的步骤来操作程序的进展。(这里有一个关于调试器如何工作的总体的解释。)
相信每个人或多或少都在用LLDB来调试,比如po
一个对象。LLDB的是非常强大的,且有内建的,完整的 Python 支持。今天我们主要介绍一个 facebook 开源的 lldb 插件 Chisel。可以让你的调试更Easy.
源码地址: Chisel
Chisel 使用 homebrew 来安装,如果你没有安装homebrew, 参考 homebrew。
12 | brew updatebrew install chisel |
安装完成按照安装日志上的提示,在~/.lldbinit
文件中添加一行,没有则新建。 提示类似如下:
123 | ==> CaveatsAdd the following line to ~/.lldbinit to load chisel when Xcode launches: command script import /usr/local/opt/chisel/libexec/fblldb.py |
做好上面的步骤,然后重启Xcode就可以尝试下了。
注:使用命令行“ls -a
”查看是否有“.lldbinit”的文件,如果没有该文件,我们可以利用“touch ~/.lldbinit”创建该文件。然后将如下图中的地址复制到文件中,保存退出。重启Xcode,暂停后在控制台输入“help”,如果出现如图二的显示则表示安装成功。(“brew uninstall”可以卸载已安装的chisel)
图一
图二
Chisel 为lldb提供了新增的便捷命令,是非常实用的命令
这个命令可以递归打印所有的view,并能标示层级,相当于 UIView 的私有辅助方法 [view recursiveDescription]
。 善用使用这个功能会让你在调试定位问题时省去很多麻烦。
使用示例:
123456789 | (lldb) pviews view<TestView: 0x18df8070; baseClass = UIControl; frame = (144 9; 126 167); layer = <CALayer: 0x18df8150>> | <UIView: 0x18df81d0; frame = (0 0; 126 126); userInteractionEnabled = NO; layer = <CALayer: 0x18df8240>> | <UIImageView: 0x18df8330; frame = (0 0; 126 126); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x18df83b0>> | <UILabel: 0x18df8460; frame = (0 135; 126 14); text = 'haha'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x18df7fb0>> | | <_UILabelContentLayer: 0x131a3d50> (layer) | <UILabel: 0x18df8670; frame = (0 155; 126 12); text = 'hahaha'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x18df8730>> | | <_UILabelContentLayer: 0x131bea10> (layer) | <UIImageView: 0x18df88d0; frame = (0 9; 28 27); hidden = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x18df8ba0>> |
这个命令也是递归打印层级,但是不是view,而是viewController。利用它我们可以对viewController的结构一目了然。 其实苹果在IOS8也默默的添加了 UIViewController 的一个私有辅助方法[UIViewController _PRintHierarchy]
同样的效果。(注:配合符号断点,也可以定位ViewController。在符号断点中输入“viewDidLoad”,便会在所有初始化ViewController时都会断一下。cmd+shift+o是快速定位)
预览效果:
1234567891011121314 | (lldb) pvc<TabBarController: 0x13772fd0; view = <UILayoutContainerView; 0x151b3a30>; frame = (0, 0; 414, 736)> | <UINavigationController: 0x1602b800; view = <UILayoutContainerView; 0x1b00aca0>; frame = (0, 0; 414, 736)> | | <FirstViewController: 0x16029c00; view = <UIView; 0x1b01e1c0>; frame = (0, 0; 414, 736)> | <UINavigationController: 0x138c5200; view = <UILayoutContainerView; 0x1316a080>; frame = (0, 0; 414, 736)> | | <SecondViewController: 0x16030400; view = <UIView; 0x2094b370>; frame = (0, 0; 414, 736)> | | | <SecondChildViewController: 0x15af6000; view = <UIView; 0x18d4e650>; frame = (0, 64; 414, 628)> | <UINavigationController: 0x1383ca00; view = <UILayoutContainerView; 0x13180070>; frame = (0, 0; 414, 736)> | | <ThirdViewController: 0x138ddc00; view = <UIView; 0x18df6650>; frame = (0, 0; 414, 736)> | | | <ThirdChild1ViewController: 0x1393fe00; view = <UIView; 0x131ec000>; frame = (0, 0; 414, 672)> | | | <ThirdChild2ViewController: 0x138dce00; view = <UIView; 0x204075a0>; frame = (414, 0; 414, 672)> | | | <ThirdChild3ViewController: 0x138a8e00; view = <UIView; 0x20426250>; frame = (828, 0; 414, 672)> | <UINavigationController: 0x160eca00; view = <UILayoutContainerView; 0x152f7d90>; frame = (0, 0; 414, 736)> | | <FourViewController: 0x13157cc0; view not loaded> |
是不是方便很多呢,而且还可以看到 viewController 是否已经 viewDidLoad .
这是个很有意思的功能,它可以让你使用Mac的预览打开一个 UIImage, CGImageRef, UIView, 或 CALayer。 这个功能或许可以帮我们用来截图、用来定位一个view的具体内容。 但是在我试用了一下,发现暂时还是只能在模拟器时使用,真机还不行。(注:visualize可以用在已打印出的对象的地址(地址也可以通过viewdebuger获取)预览该对象,例如“visualize0x7f82617107f0”)
使用简单:
1 | (lldb) visualize imageView |
fv
和 fvc
这两个命令是用来通过类名搜索当前内存中存在的view和viewController实例的命令,支持正则搜索。
如:
123456789 | (lldb) fv scrollView0x18d3b8c0 UIScrollView0x137d0c50 UIScrollView0x131b1580 UIScrollView0x131b2070 UIScrollView(lldb) fvc Home0x1393fe00 HomeFeedsViewController0x138a8e00 HomeFeedsViewController(lldb) |
这两个命令用来显示和隐藏一个指定的 UIView . 你甚至不需要Continue Progress. 就可以看到效果。
这两组命令用来标识一个view或layer的位置时用, mask用来在view上覆盖一个半透明的矩形, border可以给view添加边框。但是在我实际使用的过程中mask总是会报错,估计是有bug, 那么mask/unmask 一般不要用好了,用border命令是一样的效果,反正二者的用途都是找到一个对应的view.
这个命令会重新渲染,即可以重新绘制界面, 相当于执行了 [CATransaction flush]
方法,要注意如果在动画过程中执行这个命令,就直接渲染出动画结束的效果。
当你想在调试界面颜色、坐标之类的时候,可以直接在控制台修改属性,然后caflush
就可以看到效果啦,是不是要比改代码,然后重新build省事多了呢。
例, 其中 $122 即是目标UIView:
1234 | (lldb) p view(long) $122 = 140718754142192(lldb) e (void)[$122 setBackgroundColor:[UIColor greenColor]](lldb) caflush |
这个命令就是用来打断点用的了,虽然大家断点可能都喜欢在图形界面里面打,但是考虑一种情况:我们想在 [MyViewController viewWillAppear:]
里面打断点,但是 MyViewController并没有实现viewWillAppear:
方法, 以往的作法可能就是在子类中实现下viewWillAppear:
,然后打断点,然后rebuild。
那么幸好有了 bmessage
命令。我们可以不用这样就可以打这个效果的断点: (lldb) bmessage -[MyViewController viewWillAppear:]
上面命令会在其父类的viewWillAppear:
方法中打断点,并添加上了条件:[self isKindOfClass:[MyViewController class]]
这个命令是用来给某个对象加边框,方便与其他的视图对象有所区别。
border -c red -w 2 0x7f82617107f0(对象地址),此时模拟器会自动刷新界面并对该对象加上边框宽度为2的红色边框。
unborder 0x7f82617107f0,这条命令是去掉刚才加的边框属性
。
在控制台输入“po [
setBackgroundColor:[UIColor redColor]]”后,需要在输入“caflush”模拟器的界面才会有变化。但是不建议我们去手动改变对象的背景色,因为我们必须去改过来或者重新启动工程才能还原。通常建议是加边框来区别。0x7f82617107f0
presponder
0x7f82617107f0
,在控制器中从APPDelegate到当前对象的响应者链,不会显示同级的对象,方便我们梳理一些层级关系和响应事件的执行。
这个命令可以打印当前对象的类的继承关系。
pclass 0x7f82617107f0结果:
UITableViewLabel | UILabel | | UIView | | | UIResponder | | | | NSObject
这个命令是打印对象的继承关系、对象属性、实例方法等详细信息,其中包括我们自定义的属性和方法。
输入: pinternals 0x7fde6681dc00结果:(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}我们也可以自定义插件,不过前提是要懂一些 python。 比如设计一个打印keyWindow的windowLevel的命令:
创建python脚本文件 /magical/commands/example.py
:
1234567891011121314151617181920 |
|
其中定义了PrintKeyWindowLevel
的类,需要实现 name
description
run
方法来分别告诉名称、描述、和执行实体。
创建好脚本后,然后在前面安装时创建的 ~/.lldbinit
文件中添加一行:
1 |
|
然后重启Xcode之后就可以使用自定义的命令啦。
参考文献:
Chisel官方说明
与调试器共舞 – LLDB 的华尔兹
新闻热点
疑难解答