Reputation: 1612
I have this case where i have a static variable that changes its value somewhere across the app which causing a bug. Without going into details of the code. I need to know if there is a way to to watch this variable. Because on Xcode every time try to watch a variable it gives me this error "error: no variable named 'backend' found in this frame".
So my question is, Is there is any workaround to know which place did change the value of this variable.
Anything other than using watches because its not working for me for some reason with the error mentioned above.
Upvotes: 1
Views: 2060
Reputation: 62062
You can add a property observer to the variable (either didSet
or willSet
) and either set a breakpoint there and look at the frame when the breakpoint is hit, or you could log the current stack trace for the current thread.
For the first example, of a simple breakpoint in the property observers, I have set up the following very simple class:
class Observable {
static var someProperty: String? {
willSet {
print("Some property will be set.")
}
didSet {
print("Some property has been set.")
}
}
}
With breakpoints:
I'm simply running this code from a unit test, but you will get the same results anywhere. This is the code I'm triggering the changes with:
class ObservableTests: XCTestCase {
func testObservable() {
Observable.someProperty = "foo"
Observable.someProperty = "bar"
Observable.someProperty = "baz"
}
}
When the code runs, we'll stop right here at the break point:
I've framed this screenshot specifically as such. We see the green line and the blue arrow highlighting the breakpoint we are stopping at, but look in the left. We can see which thread we're on and how we got to this specific point. Importantly, we can click up and down through the stack.
Here, most of the frames in the stack just show us mostly unhelpful assembly code. This is what we see if we look up one frame:
But if we go up one more frame, we get to the code that modified our variable:
To be explicitly clear, the section I'm clicking through to jump to the different parts of the code that lead down to this variable being set is right here:
As mentioned, printing the stack trace is also an option, although it's significantly less effective.
If we change our willSet
and didSet
to look more like this:
class Observable {
static var someProperty: String? {
willSet {
for symbol: String in NSThread.callStackSymbols() {
print(symbol)
}
}
didSet {
for symbol: String in NSThread.callStackSymbols() {
print(symbol)
}
}
}
}
then we will be able to see the full stack trace printed in the log, as shown here:
The lines of interest from the above screen shot are these two:
2 TTDSamplesTests 0x00000001087603fa _TFC15TTDSamplesTests15ObservableTests14testObservablefS0_FT_T_ + 74
3 TTDSamplesTests 0x0000000108760492 _TToFC15TTDSamplesTests15ObservableTests14testObservablefS0_FT_T_ + 34
It's hard to decipher, but you can pick the parts out of it.
_TFC15TTDSamplesTests15ObservableTests14testObservablefS0_FT_T_
Upvotes: 6
Reputation:
One thing you can do is replace your variable with a computed property and set a breakpoint in the setter:
class Foo {
static var bar:Int = 0
}
class FooToo {
static var _bar:Int = 0
static var bar:Int {
set {
// put break point here
print("FooToo bar is set")
FooToo._bar = bar
}
get {
return FooToo._bar
}
}
}
Foo.bar = 10
FooToo.bar = 10 // -> prints "FooToo bar is set"
Upvotes: 0