智能断点

温馨提示:本文最后更新于2024-08-22 15:53:36,某些文章具有时效性,若有错误或已失效,请在下方留言

断点基础

在介绍内容之前,回顾一下设置断点和步进已暂停代码的绝对基础知识。点击任何行号都会激活该行的断点,按 Cmd+\ 也会激活当前行的断点。

当执行到该行时,断点会暂停执行,这时可以使用编辑器正下方的四个按钮来控制接下来发生的事情。

ContentView 中的代码修改为

struct ContentView: View {
    @State private var counter = 0

    var body: some View {
        VStack {
            Text("Counter is \(counter)")
            Button("Add to counter") {
                print("Preparing to add")
                add()
                print("Adding complete")
            }
        }
        .padding()
    }

    func add() {
        print("Counter was \(counter)")
        counter += 1
        print("Counter is now \(counter)")
    }
}

现在在调用 add() 时设置一个断点,然后运行代码。当执行暂停时,四个按钮会分别执行以下操作:

断点的四个按钮

  • Continue 告诉 Xcode 继续正常运行程序。当有多个断点并希望轻松跳过这些断点时,经常会用到这个功能。
  • Step Over 将跳转到当前函数的下一行代码,在本例中将跳转到 print(“Adding complete”)。当你知道问题出在这个函数的某个地方时,你就可以使用它。
  • Step Into 将移动到下一行代码,这一行代码可能在当前函数中,但如果当前行调用了不同的函数,也可能在其他地方。在本例中,它会移动到 add() 方法中,在 “Counter was ”行停止。当你不太确定问题出在哪里,所以想逐行查看代码直到找到问题所在时,你就会用到它。
  • Step Out 将退出当前函数,直到调用它的地方。如果你正在执行 “Counter was”,这将带你回到按钮的动作闭合处。当你在当前函数中到达一个点,知道问题不可能出在这里,所以想返回上一级时,你就会使用它。

当不再需要某个断点时,可以再按一次 Cmd+\,或者将其从行上拖走。另外,你也可以选择性地禁用断点,方法是再次点击断点–断点会变暗,这样你就能看到断点现在并没有激活。

条件断点

这些断点附加了一些额外的逻辑,可以通过右键单击断点并选择 “编辑断点 ”来调整。

编辑断点

编辑断点弹窗的内容,如下所示

编辑断点弹窗内容

使用条件断点的主要方法有三种:

  1. 添加明确的条件。例如,我们可以用以下条件修改 add() 断点:counter == 5。这意味着每次计数器不等于 5 时,断点都会被忽略,这对于将错误缩小到特定程序状态时很有帮助。
  2. 当多次跟踪一个错误,每次都要进行大量的步进时–如果我发现自己经常按 Continue 或 Step Over 键,只是为了让程序处于正确的状态,那么我通常会转向忽略步进。当它的值不为零时,Xcode 会忽略断点 X 次,只有当你认为快触发实际问题时才会激活断点。
  3. 既不使用条件,也不使用忽略值。从 “条件断点 ”这个名字就能看出,这些断点只有在触发某些条件时才会被触发,但事实并非如此–你可以忽略条件和忽略值,让它们每次都运行。

可以添加断点触发时要执行的操作,然后选中 “评估操作后自动继续 ”复选框。这意味着断点本身不会停止执行,但仍会执行操作。

选择性断点

可以通过点击单个断点使其变暗来禁用它们,但这只有在你想禁用某个断点时才真正有效。很多时候,你要调试的是一个特定的问题,可能是应用内购买流程、网络层或数据模型,而每个问题都在关键位置使用了多个断点。

在某些代码中添加一个只在调试时出现的条件。在当前这个简单的例子中,我们可以说有两件特别的事情想要调试:按钮操作代码运行时和 add() 方法运行时。因此,我们首先要添加以下代码,将其定义为调试模式选项:

#if DEBUG
enum DebugMode {
    case none, buttonAction, addMethod
}

let debugMode = DebugMode.buttonAction
#endif

虽然条件断点的本质意味着它们只有在直接通过 Xcode 运行时才起作用,但在这里使用 #if DEBUG 来避免将此代码发送到 App Store 也无妨。

有了这些情况,我们就可以创建一个有趣的示例,方法是设置四个断点,在四个 print() 调用中各设置一个。对于按钮操作中的两个断点,应单击 “编辑断点 ”并使用 debugMode == .buttonAction 作为条件,而对于后两个断点,则使用 debugMode == .addMethod

buttonAction debugMode

AddMethod debugMode

这意味着我们现在有了两组断点,只需修改一行代码就能激活其中任何一个。在这种情况下,你永远不会禁用断点:它们一直都处于激活状态,但由于它们的条件不同,只有在设置了正确的 debugMode 时才会真正触发。

错误断点

只要 Swift 代码中出现任何类型的错误,就会触发错误断点。要试用它们,首先在 add() 方法的末尾添加这段代码:

let task = Task {
    do {
        try await Task.sleep(for: .seconds(1))
    } catch {
        print("Caught!")
    }
}

task.cancel()

这将创建一个休眠一秒的任务,然后立即取消该任务。取消任务会产生错误,你可以看到有代码通过打印信息来处理这个错误。

现在,我要你激活断点导航器,点击左下角的小 + 按钮,然后选择 Swift Error Breakpoint。最后,按 Cmd+R 再次运行代码,并点击 “Add to counter”按钮。

添加错误断点

Swift 错误断点是一个特殊的断点,因为它会在所有尝试调用失败时自动触发,而不是附加到单行代码上。因此,如果您现在运行应用程序并点击该按钮,您会看到执行将在 try await Task.sleep(for:.seconds(1)) 行暂停–尽管我们明确捕获了该错误,但它仍在抛出,因此仍会被捕获。

错误断点触发

因此,在使用错误断点时一定要小心:如果你的代码经常抛出错误并安全地捕获它们,那么全局错误断点可能会非常嘈杂。不过,你可以在断点导航器中编辑它们,然后添加某种条件,将它们的范围缩小一点!

参考

  1. 文章来自: Hacking With Swift Plus
© 版权声明
THE END
喜欢就支持一下吧
点赞11 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容