Retain count set to 2 and no deinit called

Python doesn't guarantee this behaviour

https://docs.python.org/2/reference/datamodel.html#object.del

It is not guaranteed that __del__() methods are called for objects that still exist when the interpreter exits.

12 Likes

The Swift runtime can only guarantee that objects which it deallocates have their deinit method called. When the OS is reclaiming memory, it doesn't matter if this is because of a crash or a normal exit. Either way, it's not the runtime in charge anymore, so no Swift language guarantees are in effect.

Yes, the runtime could delay a normal exit until all (global) objects are properly deallocated, but it makes no promise to do so.

Does it say so? This is not an issue of: we don't do it cause we don't say it and people don't expect it. The runtime should deallocate all the objects before finish, otherwise, it should say so, explicitly. In the end, the runtime is creating a main function and returning a value to the OS anyway, so, the fact that it doesn't deallocate the objects in the global scope is just laziness.

To quote Larry Wall: laziness is a virtue.

I don't think this thread is going anywhere. You have an expectation, based on a simple Python example. Pretty much every language, including Python, violates this expectation.

3 Likes

It could, though.

For example, I could imagine that Swift scripts would want to make sure deinitializers run, to, say, delete some temp file.

I mean that saying "you can't rely on deinitializers" is a good thing, but saying HOW to make deinitializers run reliably is even better.

For example, do we have the absolute guarantee that a local variable in a function is deinitialized?

#!swift

class TempFile { ... }

// No deinit guarantee
let t = TempFile()

func main() {
    // deinit guarantee?
    let t = TempFile()
}
main()

The answer to the question as written is “no”. Swift cannot guarantee this because you can escape references to these objects nearly anywhere.

There is no general question you can ask about reference counted objects in Swift that can be answered with “the deinitializer is definitely called here”. Whether a deinitializer is called is dependent on the entire history of the program execution, as well as the exact behaviour of the program in question. You can ask that of specific code samples, where the answer can be drawn from the semantics of the language as best as possible.

But as you note, Swift’s guidance on deinit is the same as Python and Java: in general, you shouldn’t rely on deinit to clean your resources up. If you need deterministic cleanup, you should write a with-style function that will do that cleanup.

7 Likes

It might, and it should.

Created by the language. As you saw in a previous reply about Python, they make it clear you can't rely on destructors when the interpreter exists. Swift documentation on the other hand says nothing about it.

So, there is at the very least two places where this could go:

  1. The documentation could be updated to say deinitializers won't run for global objects, or they may run, or whatever the compiler decides to do.
  2. The compiler could be updated to handle the case of global objects.
3 Likes

Where does it says this? I've looked for it and didn't find it.

What's "it"? Swift doesn't have a language specification. There is no concrete reference on when deinitializers run. I appreciate that I wasn't clear there: by "Swift's guidance" I mean "the guidance of the Swift community", just as I meant with the Python and Java examples.

1 Like

"It" being the guidance, yes. I went through the documentation (basically this), and found nothing like that.

Relying on the community is not a very good idea, since the community does, inherently, have different views on what's expected and what's correct.

I see your point tho. Thanks

If you have a static property in a Swift file I don’t think it gets deinited on process exit, either. Or if you have any kind of top-level object that contains children objects they’re not going to get automatically deinited. You’d have to do a bunch of work to keep track of all top-level objects (how do you even distinguish them?).

One might argue it’d be nice to know that deinit will always be called for everything that was inited, but that’d be an enormous runtime burden and isn’t how the system around Swift works. Cocoa processes can be killed at any timeÂč by the system, in general. This is a good thing — it makes the system much snappier for the user.

Also, as has been pointed out several times, processes go away for other reasons that clean exits, so any, say, files you create need a better solution than trying to remove them in a deinit call.

If you explicitly want to clean stuff up when you quit an app make your own little pool of things to clean — if you’re on Cocoa systems you can turn off sudden termination when you have anything in the pool waiting, and then flush out the pool in the notifications that the app will terminate.

-Wil

Âč Yes, the developer can permanently turn off sudden termination, but it’s bad practice.