13

In Xcode's Variables View, on the right of the Debug area, when an app is running and paused at a breakpoint you can right-click a variable and select "Edit Value".

For a swift String it's greyed out, and I can imagine why that might be the case. But even for a simple int, it brings up an edit box to enter an new value, but after hitting the value stays at the old value. This is true even for a var which is changed during the code.

Update: as shown in Jim's answer below, you should be able to set the value using the lldb expression command, but, although Xcode will tell you it has changed, it fails to actually change the value.

Is this broken, or is there something specific you need to do for it to work? Thanks.

Screenshot

Update: It's a compile bug - see Jim's comment. Here's a workaround...

    println("Before lldb change, foo is \(foo)")
    //make compiler think foo may change, so I can change it myself at the console
    if count("abcd") == 0 { foo = 0 }
    println("After lldb change, code now thinks foo is \(foo)")
Community
  • 1
  • 1
DenverCoder9
  • 3,635
  • 3
  • 31
  • 57

2 Answers2

21

Most Swift entities, for sure strings but even "simple" types like Int's, are actually not simple types. The values you see in the Variables View are constructed by data formatters in lldb that are set up to present a useful view of the Values without running any code (for performance reasons.) They know how grub around & fetch the contents of Swift types, but we didn't teach them how to edit values, only present them.

If you want to change a value, you need to run some code in your program to do that, which you can do using the expression command in the lldb console. So for instance if you have an Int variable called foo, you can do:

(lldb) expr foo = 12

That will compile & execute that code fragment, and of course the Swift compiler does know how to alter these Swift values, so the resultant code will correctly set the value.

Note, it does appear that the swift compiler will sometimes copy a value to a register and use it from the register w/o indicating that fact in the debug info. If that happens, lldb will report the value it set in the location the debug information pointed to, but the code will actually use the temporary value. I've filed a bug with the swift compiler demonstrating one instance of this.

Jim Ingham
  • 25,260
  • 2
  • 55
  • 63
  • 1
    This causes more strange behaviour! Although the value on the Variables View, and '(lldb) po foo', will now say 12, the value doesn't change in the actual program. Subsequent println commands in code will still print the old value, and if statements are executed according to the old value. – DenverCoder9 Jul 15 '15 at 10:03
  • Note: this is true even if I set the original value using something like 'var foo = count(testString)', so the compiler doesn't 'know' what the value will be. – DenverCoder9 Jul 15 '15 at 10:13
  • Don't do 'var foo = count(testString)'. That creates a NEW local variable that persists for the duration of the expression evaluation. Leave the 'var' off and it should work. – Jim Ingham Jul 16 '15 at 20:01
  • Sorry for not being clear - the 'var foo = count(testString)' was in the code, not the console. Am typing '(lldb) expr foo = 12' in the console, the console says the value has changed, the Variables View says the value has changed, the app ignores it and keeps using the old value. – DenverCoder9 Jul 17 '15 at 11:45
  • Added screenshot to OP. – DenverCoder9 Jul 17 '15 at 12:00
  • 4
    Interesting. That's a bug in the compiler. Since it knows foo isn't changed between the time you set it and used it, it stores the value in some temporary location - most likely a register - and accesses it out of the register. That's a fine thing to do, but it neglects to tell the debugger that the live version is now in a register. You can tell this because if you make an func that takes an inout Int, and pass foo to that call between the time you set the variable and read it, then the code path will be affected by your "set". – Jim Ingham Jul 17 '15 at 18:20
  • Thanks. :) I've tested a workaround & added to OP. If you update your answer to include the above comment (or make a new one) I can mark it accepted. – DenverCoder9 Jul 18 '15 at 11:20
  • Just in case, expr x = 12 if x is a constant creates temporary variable $Rx with no effect on actual values – user1232690 Sep 05 '16 at 15:12
2

In case of constant Integers, you can't directly set anything.

However let's say you have variable passed as a parameter to your function:

value = (AnyObject!) Int64(38) instance_type = (Builtin.RawPointer) 0xb000000000000263

Note that this address starting with 1 is not a real pointer. Now let's say you want to replace value 38 with 64. You type:

po Unmanaged.passUnretained(64).toOpaque() and you've got magical pseudo address of constant 64:

0xb000000000000403 - _rawValue : (Opaque Value)

then you replace 0xb000000000000263 with 0xb000000000000403 and you've got your constant changed.

God, I love Swift

user1232690
  • 481
  • 5
  • 16