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
.