-
-
Notifications
You must be signed in to change notification settings - Fork 77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Promise does not deinit #9
Comments
Future investigations, removing in self.progressBlocks.append({ p in
progress(p)
}) totally heal leaks
|
@mandarin6b0 Thanks a ton for your time and investigations, |
@mandarin6b0 Indeed I can reproduce on my side. Have no idea how we didn't see this issue earlier :) self.progressBlocks.append({ p in
progress(p)
}) by self.progressBlocks.append({ p in
//progress(p)
}) solves this issue. So our problem here seems to lie in capturing the I have no solution right now but I'm on it ! :) If you have any ideas, feel free to share. Have a good day, |
@s4cha, thank you for reply. Solved cycle by changing in p(resolve:resolvePromise, reject:rejectPromise, progress:progressPromise) to p(resolve: { [weak self] v in self?.resolvePromise(v) },
reject: { [weak self] v in self?.rejectPromise(v) },
progress: { [weak self] v in self?.progressPromise(v) }) There may be more places with cycled refs, need some more time for audit. |
I did it on swift 3 and had the same conclusions, I will try your fix tomorrow. That sounds awesome!! Great work @mandarin6b0 |
Hey @mandarin6b0, I found that replacing this : let p = Promise<X> { resolve, reject in
switch self.state {
case let .fulfilled(value):
let x: X = block(value)
resolve(x)
case let .rejected(error):
reject(error)
case .pending:
self.successBlocks.append({ t in
resolve(block(t)) //Because P1 captures P2's resolve block
})
}
}
p.start() by this : let p = Promise<X>()
switch state {
case let .fulfilled(value):
let x: X = block(value)
p.resolvePromise(x)
case let .rejected(error):
p.rejectPromise(error)
case .pending:
self.successBlocks.append({ t in
p.resolvePromise(block(t))
})
}
p.start() in By the way, what you found can also be shown with the code below : func foo() {
let a = Promise<String> { r, _ in
Timer.scheduledTimer(withTimeInterval: 3, repeats: false, block: { _ in
r("TOTO")
})
}
a.then{ s in
print("Doing 1 thing")
}
} In essence, if self.successBlocks.append({ t in
resolve(block(t)) //Because P1 captures P2's resolve block
}) which seems to cause the retain issue. We're getting closer :) Cheers, |
@mandarin6b0 The retain cycle is, as you noticed earlier, in part due to Indeed each promises captures the next ones The fix Since it's a weak reference, it might actually be released before we even use it! :( For instance when we're using promiseA().registerThen { s in
print("Doing 1 thing")
}.then {
print("Doing 2 things")
} would not work unless we write it like so : let a = promiseA()
a.registerThen { s in
print("Doing 1 thing")
}.then {
print("Doing 2 things")
} Because nothing retains So it made me think, what if we release the initial promise ourselves, but only once we don't need it anymore. And this solves the retain cycle, and the tests pass \o/. :) Cheers |
@mandarin6b0 here is the diff that fixes the issue on my side : It essentially is :
This is on swift 3 but should be the same for swift 2. Let me know how it works for you and we'll update the version :) Have a fantastic day and thanks again for your work 👏 !!! |
@s4cha something is still wrong on my side, I applied your diff: |
@mandarin6b0 Indeed :/ for some reason this works : let a = Promise { r, _ in
Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { _ in
r()
})
}
a.then { print(".") } while this doesn't : let a = Promise { r, _ in
r()
}
a.then { print(".") } aka async vs sync Back to work then ^^ |
@mandarin6b0 of think I got it, look like moving diff --git a/Source/Promise.swift b/Source/Promise.swift
index 9799cd5..dc5ca36 100644
--- a/Source/Promise.swift
+++ b/Source/Promise.swift
@@ -78,8 +78,8 @@ public class Promise<T> {
resolve(block(t))
})
self?.failBlocks.append(reject)
+ self?.progressBlocks.append(progress)
}
- self?.progressBlocks.append(progress)
}
}
p.start() Is it ok on your side ? |
Here is a sample project with your issue :) |
@s4cha yes, moving progress block fix it. All ok now on my side :) |
Hey @mandarin6b0 this is now live in 2.0.1 -> https://github.com/freshOS/then/releases/tag/2.0.1 |
@s4cha, 👍 Thanks you for your work! Then I'm closing this issue :) |
In Promise.swift add debug outputs
Then if you run
The output will be
After adding
[weak self]
to allregisterThen<X>
blocksand changing
passAlongFirstPromiseStartFunctionAndStateTo<X>
toThe output:
There are still strong ref cycles somewhere, any ideas?
The text was updated successfully, but these errors were encountered: