class Person {
deinit {
print("Person deinit")
}
}
class Student {
deinit {
print("Student deinit")
}
}
func run() {
let person = Person()
var student = Student()
print("run end")
}
run()
output result:
Encountered a problem, in release mode, let variables should be released before the end of "run end" like var variables, why are let variables released after "run end"?
Is there any reason here?
Are you suggesting there is a bug or are you just curious why it is so? AFAIK nothing in the language specifies guarantees in this behaviour, so even two vars or two lets can behave differently. IIRC there was a (pre?) pitch thread here a couple of months ago that proposed to make variable lifetime more deterministic, until that - it is what it is.
What's quite interesting is that you can see it statically. I made a little demonstration using Godbolt.
In 5.0 and 5.1, the generated code looks like this:
main:
push rbp
mov rbp, rsp
call run_end@PLT
call in_student_deinit@PLT
call in_person_deinit@PLT
xor eax, eax
pop rbp
ret
But in 5.2, it changed to this, for some reason swapping the order of "run end" and the student's deinit:
main:
push rbp
mov rbp, rsp
call in_student_deinit@PLT
call run_end@PLT
call in_person_deinit@PLT
xor eax, eax
pop rbp
ret
Which is where it stayed all this time, including in 5.5 (the latest release on Godbolt). However, running it against nightly shows it has since been fixed:
main:
push rax
call run_end@PLT
call in_student_deinit@PLT
call in_person_deinit@PLT
xor eax, eax
pop rcx
ret
Both let and var instances are declared inside function scope. So in mental model they should both deinit after print("run end"). I suppose ARC optimizations come into play when -O optimization enabled, so let instance is deallocated earlier.
Thanks for your answer.
Yes, according to WWDC Session 10216, in release mode, it is true, if the variable is not used, it will be released as early as possible. So the let variable should also be released in advance. In fact, it is released after the "run end". Curious, what is the reason for Apple's design?
Thanks for your answer.I'm just curious, according to WWDC Session 10216, in release mode, if the variable is not used, it will be released as early as possible. So the let variable should also be released early, in fact it is released after the "run end".
It’s a bit subtle, but by my interpretation that WWDC session doesn’t contradict the behavior here. The speaker notes that “depending on the ARC optimizations that kick in, the observed lifetimes may differ from their guaranteed minimum” (4:50). Furthermore, the following section describes that precise object lifetimes should not be relied upon—beyond the guarantee of “strong reference will not be released before last use,” Swift does not currently promise much about object lifetimes. Though, as @tera notes, some moves have been made towards tightening guarantees in this area in future versions of Swift.
Xcode 13 provided a Swift build setting called “Optimize Object Lifetimes” that’s not available in Xcode 14. If your project already customized this build setting, it now becomes a user-defined setting. It has no effect and you can remove it. Xcode 14 now consistently optimizes object lifetimes. (91971848)