Reputation: 13335
I have an issue with strong references in one of my view controllers which is causing a memory leak. First, my setup:
2 view controllers (v1 and v2). v1 segues to v2, and v2 has a close button that pops itself back to v1. v2 contains code that tries to reconnect infinitely until a connection is made. (video streaming using red5pro). Here is the code:
func reconnect(){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(5.0 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { () -> Void in
self.connectToStream()
}
}
The continuous reconnecting is desirable in my situation, but when the user exits v2, I want the reconnecting to stop. But currently, the reconnect goes on infinitely, even when the user has left v2.
I have come to know that this is because v2 has strong references and continues to live even after the user exits from it. So this is causing the code that is infinitely calling the reconnect() method to continue running. I'm going to try to clean up v2 to convert everything to weak references, but I am also considering some alternatives, and I had a couple questions regarding it:
Is there a way to kill the reconnecting on viewDidDisappear or something, so even if my view controller doesn't get destroyed, at least my reconnecting process stops?
After exiting from v2 back to v1, if the user again segues to v2, is it possible to assign the same instance of v2 rather than creating a new instance of it each time?
Upvotes: 1
Views: 996
Reputation: 437532
The dispatch_after
cannot be canceled, but there are a couple of options:
Use weak references, which will allow self
to be deallocated:
func reconnect() {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(5.0 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { [weak self] in
self?.connectToStream()
}
}
This admittedly keeps the timer going, but prevents it from retaining the view controller, so the view controller is released and connectToStream
will not be called.
Use NSTimer
and cancel it when the view disappears:
weak var timer: NSTimer?
func reconnect() {
timer?.invalidate()
timer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: #selector(handleTimer(_:)), userInfo: nil, repeats: false)
}
func handleTimer(timer: NSTimer) {
self.connectToStream()
}
override func viewDidDisappear() {
super.viewDidDisappear()
timer?.invalidate()
}
Note, because this selector
-based NSTimer
keeps a strong reference to its target
, you cannot cancel in deinit
(because there's a strong reference cycle). So you have to find some other appropriate event to resolve this (e.g. viewDidDisappear
).
Upvotes: 3