Firstly: the defer gets executed as you can clearly see when adding a print(str) to it.
Now to explain why the returned value does not reflect the changed value:
The reason for this is that String is immutable - whenever you write str += something you create an entirely new String instance and store it inside str.
If you write return str that returns the current instance of str which is 123yyy4. Then the defer gets invoked and assigns the completely new and unrelated String 123yyy4xxx to str. But that does not change the previous String object stored inside str, it simply overwrites it and therefore does not affect the return which has already "happened".
If you change your method to use NSMutableString instead you will always operate on the same instance and the result will therefore correctly output 123yyy4xxx:
func branch() -> NSMutableString {
var str = NSMutableString()
defer { str.appendString("xxx") }
str.appendString("1")
let counter = 3;
if counter > 0 {
str.appendString("2")
defer { str.appendString("yyy") }
str.appendString("3")
}
str.appendString("4")
return str
}
let bran1 = branch()
In that code the return returns the instance stored in str and the defer alters that instance, it does not assign a new instance but changes the already present one.
For explanation sake you can try to view the memory address of str at different stages:
- at the time of the
return
- before changing
str in the defer block
- after changing it
For the NSMutableString all three cases will yield the same memory address meaning that the instance stays the same. The String one however prints two different memory addresses resulting in the returned String to point to someAddress and the deferred one to point to someOtherAddress.