First: defer is executed, as you can see, adding print(str) .
Now, to explain why the return value does not reflect the changed value:
The reason for this is that String is immutable - whenever you write str += something , you create a completely new instance of String and save it inside str .
If you write return str , which returns the current instance of str , which is 123yyy4 . Then defer is defer and assigns a completely new and unrelated String 123yyy4xxx str . But this does not change the previous String object stored inside str , it just overwrites it and, therefore, does not affect the return , which has already "occurred".
If you change your method to use NSMutableString , instead you will always work on one instance, and therefore the result will 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 this code, return returns the instance stored in str , and deferral modifies this instance, it does not assign a new instance, but modifies an existing one.
For clarification purposes, you can try to look at str memory address at different stages:
- during
return - before changing
str in defer block - after changing it
For NSMutableString all three cases will result in the same memory address, which means that the instance remains the same. However, String prints two different memory addresses, as a result, the returned string points to someAddress , and the delayed one to someOtherAddress .