Orion98mc
(Thierry Passeron)
1
Hi everyone,
I have a struct that is referencing (as weak ref) a parent container object and the container itself holds this struct value. If I print the struct to see what it has in it then the container never gets de-inited when it should. I am sorry if this may seem lame but can anyone teach me why this is happening? Please find below a simple code that helps demonstrating my issue.
class Container {
var foo: Foo!
deinit {
print("deinit \(self)")
}
init() {
foo = Foo(name: "Foo!")
foo.container = self
}
}
struct Foo {
weak var container: Container!
let name: String
init(name: String) {
self.name = name
}
}
do {
let c = Container()
print(c)
print("Should deinit c...") // (1)
}
do {
let c = Container()
print(c.foo) // <----- culprit ?
print("Should deinit c...") // (2)
}
If I run this on my Mac (macOS Mojave 10.14.2) using Xcode 10.1 I see the first deinit message after (1) but not after the (2).
What's wrong?
Thanks for your help.
Thierry
AlexanderM
(Alexander Momchilov)
2
Orion98mc
(Thierry Passeron)
3
Nope. This is the result I get in a macOS test target in Xcode 10.1 I just removed the enclosing "func test_...() {"
Orion98mc
(Thierry Passeron)
4
@AlexanderM I've just read the link you gave me on SO... just to be clear I've decided to make this test because I've seen weird behaviours in an iOS app I am writing where "containers" were not released just because I printed the description of embedded structs (with weak refs). So it's really not linked to the Playgrounds.
If you breakpoint, run it until right before it exits, and then click the Debug Memory Graph button in Xcode, it thinks it leaks as well (note: Reproducible by replacing the print with let _ = String(describing: c.foo) as well)
Orion98mc
(Thierry Passeron)
6
That's relief to see Xcode gets it as well :)
Note that if you replace "struct Foo" with "class Foo" everything works as expected.
Few things I found:
- This is fairly new, the code works properly on the
Swift 4.1 Snapshot 2018-05-31 toolchain I had lying around on my computer
- Here's the most minimal version I could get that breaks:
class DeinitDetector {
deinit {
print("Deinit!")
}
}
struct WeakHolder {
weak var a: DeinitDetector!
}
do {
let a = DeinitDetector()
let b = WeakHolder(a: a)
let _ = String(describing: b)
}
I created an issue on the issue tracker here
Orion98mc
(Thierry Passeron)
8
@TellowKrinkle ok thanks for the information. So a regression it seems... Hope it gets fixed soon.
I tried it with Swift Development Snapshot 2018-11-13 (a), it works properly. So it seems to be fixed.
1 Like
Orion98mc
(Thierry Passeron)
10
Great news. However I'll have to wait 'till it gets mainstream since distribution on the App Store is bound to Xcode's swift versions. But thanks for the check! Let's hope they add a regression test for this issue in Swift's tests because it's such a vicious bug...
ole
(Ole Begemann)
11
It looks like @Mike_Ash did indeed add a test. Here's his pull request.
Orion98mc
(Thierry Passeron)
12
@ole thanks for the follow up. @Mike_Ash thank you as well!
But shouldn't the test be made on StructHasNativeWeakReference rather than NativeSwiftClassHasWeak to test this very problem (or a new test be added) ? I noticed that class objects don't get retained in my case.