Xcode之断点调试

iOS进阶

Xcode这个开发工具的使用相信每一个iOS开发者都已经可以熟练使用,但是要说到所有功能都详细了解,想必人数不多,这篇文章主要介绍xcode的断点调试功能。

##简介

Xcode断点调试

上图是我们使用Xcode进行断点调试时,上图底部图标从左到右功能分别如下:

  • 启用/禁用断点(点击后变灰色,所有断点失效;再点击变蓝色,所有断点生效)
  • 继续执行程序(点击后跳过本次断点,继续执行程序)
  • 执行下一步(点击后执行第23行代码)
  • 进入方法(点击后进入-testLog方法)
  • 跳出方法(在-testLog方法内部点击后回到第22行代码)

除了这些功能,我们还可以编辑断点,在断点出右键选择Edit BreakPoint

下面我们来分别介绍下这几个选项以及如何设置这些选项:

Condition

Condition表示断点条件。开发者可以在Condition输入框中设置触发断点的条件。比如上面for循环的例子我们可以在Condition条件中添加条件

这样我们的断点只有在index==5时才会被触发,这样就有利于我们在某些for循环时只有在满足某个条件时才触发断点。

Ignore

Ignore为忽略次数,同样我们在上面for循环的例子中可以将Ignore设置为5,那么断点第一次触发时index=6时。

Action

Action为触发动作。Action可以添加多条,在触发断点后,会紧接着执行设定的Actions。Action有6种执行类型,其中较常用的有Debugger Command和Log message。

Debugger Command

Log Message

当然log的信息还可以读出来

试一下吧,有惊喜!!!

Options

Options控制在执行断点对应的Actions后是否自动继续执行程序。勾选后Options后,断点被触发后不进入Debug界面。

接下来看下断点的正题

断点

断点类型

我们在Xocde中添加断点时有几种类型的断点:

下面我们来介绍下我们常用的集中类型的断点:

Exception Breakpoint(异常断点)

当我们添加了一个Exception BreakPoint 时,实际上是添加了一个全局断点

正常情况下我们的代码崩溃后,如果没有全局断点 代码会崩溃在main函数中,但是我们如果添加了全局断点,代码发生崩溃时,就会自动崩溃到出现问题的哪一行代码,比较方便我们去定位问题。

Symbolic Breakpoint (符号断点)

当我们添加一个符号断点时,会自动为我们弹出自定义面板,

与普通断点相比,符号断点的编辑界面多出来Symbol和Module两个输入框。下面我们来看下这两个输入框的作用。

Symbol

可以在Symbol输入框中设置断点出发方法/函数。在Symbol中设置一个方法/函数后,运行程序并执行到此方法时会触发断点。

如果是C语言方法那么直接使用方法名就可以

Module

可以在Module输入框中设置Symbol中的函数所在的库,以避免不同库中存在名字相同的方法/函数,默认不用填写。

Condition

与普通断点的用法基本一致
在Condition输入框中设置$arg3==nil,就会限制断点在满足第一个参数和第二个参数都为nil时才会被触发。但是实际上使用下面这种写法才可以
[(NSString *)$arg3 length] == 0

这里我们可以用来判断某个方法再被调用时,哪里的参数传递是有问题的。

Watch Breakpoint(监控断点)

有时候我们需要监听某个变量的值的变化

添加断点

断点结果

很可惜没有发现监听数组个数变化的断点

断点的应用

查看UI控件约束冲突

我们先看下下面这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (void)addVCSubView {
UIView *contentView = [[UIView alloc] init];
contentView.backgroundColor = [UIColor blueColor];
[self.view addSubview:contentView];
[contentView mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self.view);
make.width.height.equalTo(@200);
}];


UIView *view = [[UIView alloc] init];
view.backgroundColor = [UIColor greenColor];
[contentView addSubview:view];
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.equalTo(contentView);
make.centerY.equalTo(contentView);
make.top.equalTo(contentView.mas_top).offset(20);
make.width.height.equalTo(@100);
}];
}

具体的展示如图:

从图中我们可以很明显的看到绿色的子视图的宽高并不相同,同样在控制台上我们也看到了这样的输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2020-08-02 10:16:30.755508+0800 APPLaunchTime[1629:28678] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<MASLayoutConstraint:0x600000e183c0 UIView:0x7fa513e11cc0.height == 200>",
"<MASLayoutConstraint:0x600000e181e0 UIView:0x7fa516604e70.centerY == UIView:0x7fa513e11cc0.centerY>",
"<MASLayoutConstraint:0x600000e18600 UIView:0x7fa516604e70.top == UIView:0x7fa513e11cc0.top + 20>",
"<MASLayoutConstraint:0x600000e186c0 UIView:0x7fa516604e70.height == 100>"
)

Will attempt to recover by breaking constraint
<MASLayoutConstraint:0x600000e186c0 UIView:0x7fa516604e70.height == 100>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.

控制台提示我们 下面列表中的约束其中有一个是不需要的,同时当前展示的样子是系统通过移除了哪个约束后展示出来的

约束问题是什么

我们先看下控制台输出的提示

1
2
3
4
"<MASLayoutConstraint:0x600000e183c0 UIView:0x7fa513e11cc0.height == 200>",
"<MASLayoutConstraint:0x600000e181e0 UIView:0x7fa516604e70.centerY == UIView:0x7fa513e11cc0.centerY>",
"<MASLayoutConstraint:0x600000e18600 UIView:0x7fa516604e70.top == UIView:0x7fa513e11cc0.top + 20>",
"<MASLayoutConstraint:0x600000e186c0 UIView:0x7fa516604e70.height == 100>"

上面的提示中设计到两个UIView对象,0x600000e183c00x7fa513e11cc0,通过查看上面的约束提示,我们发现height,centerY,top,height 这几个约束都是垂直方向的约束。

上面的提示还有下面这句

1
2
Will attempt to recover by breaking constraint 
<MASLayoutConstraint:0x600000e186c0 UIView:0x7fa516604e70.height == 100>

尝试通过break高度为100的约束来正确展示这个视图,而结合我们上面展示的图片 没有生效的约束是height=100与上面的描述一致。

那通过上面的分析我们得出这次约束的问题是:

控件0x600000e183c00x7fa516604e70在垂直方向存在约束冲突,目前系统通过移除UIView:0x7fa516604e70.height == 100约束来展示UI,如果系统的修改与你的预期不符,可以通过修改上面提到的四个约束中的一个来展示出正确的UI。

哪个视图约束有问题

分析出约束的问题后,我们需要定位到底是哪两个视图出现了约束问题。

通过内存地址定位

我们可以通过查看层次结果,然后通过出现约束问题的视图的内存地址进行筛选,这样我们就能容易的定位到出现问题的视图。

通过lldb命令

我们还可以通过设置出现问题的视图的背景颜色来定位到底是哪个视图出现问题,当然是用lldb命令的前提是我们需要在合适的地方添加断点。

首先我们需要在项目中添加约束冲突(符号断点)断点,添加方法如下

添加了这个断点后,在应用启动遇到约束冲突的位置系统会,直接有约束冲突的位置设置断点,下面截取一部分发生断点时的提示

1
2
3
4
*UIButton:0x10b091670'注册/登录'- AMBIGUOUS LAYOUT for UIButton:0x10b091670'注册/登录'.minY{id: 159}   UIButtonLabel:0x10b15fb60'注册/登录'

*UILabel:0x10b08f790'群组'- AMBIGUOUS LAYOUT for UILabel:0x10b08f790'群组'.minX{id: 136}, UILabel:0x10b08f790'群组'.minY{id: 138}, UILabel:0x10b08f790'群组'.Width{id: 135}, UILabel:0x10b08f790'群组'.Height{id: 140}

这里我们可以通过关键词 AMBIGUOUS LAYOUT 来获取所有存在约束冲突的位置,因为log中有了按钮或者label的文案我们可以很快的定位到具体位置。

当然 如果层次非常深,或者我们无法通过文案进行区分,我们还可以通过下面的命令修改视图的背景颜色来定位出现约束冲突的视图。

1
expr ((UILabel *)0x10b091670).backgroundColor = [UIColor yellowColor];

结果如下图:

通过上面的方法我们可以定位到出现问题的视图控件,那么我们 下一步就要看如何去解决这个约束冲突。

总结

通过上面的方法我们可以准确的定位出出现约束冲突的控件是什么,约束冲突的原因是什么,那么接下来我们就需要根据自己的场景以及控制台给出的提示去判断目标的布局是什么样的,怎么去改。

参考文章

How to trap on UIViewAlertForUnsatisfiableConstraints?

有歧义(AMBIGUOUS LAYOUT)的约束布局调试方法