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

Time:04-02

my code is very simple, in ContentView.swift file:

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
            }
    }
}

Run the code, it will print tab0.name is "tab0", that's right.

But if you add a breakpoint at Line 1, then interrupted at the breakpoint, enter "po tab0.name" in debug console, you will see tab0.name is equal to "":

(lldb) po tab0.name
""

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

Why is that? Am I misunderstanding something? Thanks! ;)

CodePudding user response:

Note that global variables and static properties are computed lazily (see the Language Guide on this):

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.

When the breakpoint is hit, "Line 1" is not run yet, so the global constant tab0 is no yet computed, and so its value is all zeros. When interpreted as a String, that represents the empty string.

You can verify this by adding an initialiser that prints something:

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()
        }
    }
}

and see that it is not called until after the print("tab0.name is \(tab0.name)") line. Output:

ContentView!
init!
tab0.name is tab0

But that's only half of the picture. Why doesn't evaluating tab0.name in LLDB call the getter of tab0 and cause it to be initialised?

I didn't find any documentation for this, but I suspect that this is a quirk of LLDB. I suspect that when you do po tab0.name, LLDB directly accesses the area of memory where tab0 is stored, rather than call its getter (as an optimisation perhaps?). It knows where tab0 is because you are currently in the same file as where tab0 is declared. If tab0 were declared in another file, then po tab0.name would cause tab0 to be initialised as you would expect:

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

which is why I suspect that this is just a quirk of LLDB.

  • Related