为什么 swift 文件中的全局变量在调试时看起来没有正确初始化?

Why global variable in swift file doesn't look like initialized correctly at debugging?

我的代码很简单,在ContentView.swift文件中:

import SwiftUI

struct NavTab {
    let name: String
}

let tab0 = NavTab(name: "tab0")

struct ContentView: View {
    var body: some View {
        Text("Hello, world!")
            .onAppear {
                print("tab0.name is \(tab0.name)")     // Line 1
            }
    }
}

运行 代码,它会打印 tab0.name is "tab0",没错。

但是如果你在第1行加了一个断点,然后在断点处中断,在调试控制台输入"po tab0.name",你会看到tab0.name等于"":

(lldb) po tab0.name
""

(lldb) po tab0
▿ NavTab
  - name : ""

这是为什么?我误会了什么吗?谢谢! ;)

请注意,全局变量和静态属性是延迟计算的(请参阅此处的 Language Guide):

Global constants and variables are always computed lazily, in a similar manner to Lazy Stored Properties. Unlike lazy stored properties, global constants and variables don’t need to be marked with the lazy modifier.

Local constants and variables are never computed lazily.

当遇到断点时,“第 1 行”还不是 运行,因此全局常量 tab0 尚未计算,因此其值全为零。当解释为 String 时,表示空字符串。

您可以通过添加打印内容的初始化程序来验证这一点:

struct NavTab {
    let name: String

    init(name: String) {
        self.name = name
        print("init!")
    }
}

@main
struct testApp: App {
    var body: some Scene {
        print("ContentView!")
        return WindowGroup {
            ContentView()
        }
    }
}

并看到直到 print("tab0.name is \(tab0.name)") 行之后才调用它。输出:

ContentView!
init!
tab0.name is tab0

但这只是图片的一半。为什么在 LLDB 中评估 tab0.name 不调用 tab0 的 getter 并导致它被初始化?

我没有找到这方面的任何文档,但我怀疑这是 LLDB 的一个怪癖。我怀疑当你po tab0.name时,LLDB直接访问存储tab0的内存区域,而不是调用它的getter(作为一个也许优化?)。它知道 tab0 在哪里,因为您当前在与声明 tab0 的位置相同的文件中。如果 tab0 是在另一个文件中声明的,那么 po tab0.name 将导致 tab0 像您期望的那样被初始化:

(lldb) po tab0.name
init!
"tab0"

这就是为什么我怀疑这只是 LLDB 的一个怪癖。