How to avoid retain cycles when using OSCServer
jcurtis-cc opened this issue · 15 comments
Hi Devin,
Swift isn't my main language, I'm hoping you could make this clearer for me.
I'm instantiating an OSCServer inside a VC, which I only need while I'm inside that VC. I want to close that connection and deallocate the server when I leave the VC and return to the parent VC.
By making the server.delegate = self
I cause a retain cycle even if I try setting server.delegate = nil
in viewWilDisappear()
I've tried declaring my server as weak
and unowned
What am I missing?
In OSCServer.swift,
open class OSCServer {
// ...
open var delegate: OSCServerDelegate?
// ...
}
delegate
is holding a strong reference here.
open var delegate
should be open weak var delegate
Thanks for the speedy response @orchetect - I had this suspicion, nice to have that confirmed! I'm still getting a retain cycle after adding weak
there. I can't understand what I'm doing wrong here :|
There could still be retain cycles elsewhere in SwiftOSC or in your code.
That was just the most obvious one I spotted.
Thanks @orchetect - think i've tried every variation of weak
and unowned
in every reference to the delegate I can. Removing the instantiation of the OSCServer
in this view controller stops the memory leak. I can't figure out how to deallocate it. The VC is deiniting but the OSCServer stays allocated even when setting it to nil
- so every time the VC is pushed, a new OSCServer ends up on the stack
As a workaround I'm instantiating the OSCServer
at the top level in appDelegate
like one of Devin's examples and setting that VC to the delegate (using self
). I can't deallocate it but I can stop it and start it. I'm working on an app that's sensitive to memory so I'm hoping for a fix!
I'm sure this is something very obvious to Devin that I just can't see :)
Don't rule out the possibility that I screwed up somewhere.
In OSCServer.swift line 44.
func run() {
DispatchQueue.global().async{
while true {
let (data,_,_) = self.server.recv(9216)
if let data = data {
if self.running {
let data = Data(data)
self.decodePacket(data)
}
}
}
}
}
Probably shouldn't be while true
. Try changing that to while self.running
. Then open func start() needs to have run() in it .Or something like that.
I don't have time to dig into this right now.
Wow. That was some bad programming.
Awesome, thanks for the quick response Devin - I'll test this and make a PR if it works
It might not completely solve the issue. But there are definitely issues with that block.
Also be aware that async { }
closure is holding a strong reference to self
.
I'd start by adding [weak self]
to the closure.
Maybe I should just pitch @orchetect 's OSC library. https://github.com/orchetect/OSCKit
He doesn't include the network layer so it's not plug and play but if you're good with that stuff it should be pretty easy. I know he's done some pretty extensive testing and uses it daily.
Also, If you check out the dev branch, I was working on using a different Swift Network framework. Maybe try giving that a spin. Full disclosure it's been a while and I'm not sure how well it works.
Pretty much the entire OSCServer is completely different.