Say you have an everyday CADisplayLink
class Test: UIViewController {
private var _ca : CADisplayLink?
@IBAction func frames() {
_ca?.invalidate()
_ca = nil
_ca = CADisplayLink(
target: self,
selector: #selector(_step))
_ca?.add(to: .main, forMode: .commonModes)
}
@objc func _step() {
let s = Date().timeIntervalSince1970
someAnime.seconds = CGFloat(s)
}
Eventually the view controller is dismissed.
Does anyone really definitively know,
do you have to explicitly call .invalidate()
(and indeed nil _ca) when the view controller is dismissed?
(So perhaps in deinit, or viewWillDisappear, or whatever you prefer.)
The documentation is worthless, and I'm not smart enough to be able to look in to the source. I've never found anyone who truly, definitively, knows the answer to this question.
Do you have to explicitly invalidate, will it be retained and keep running if the VC goes away?
A run loop keeps strong references to any display links that are added to it. See add(to:forMode:)
documentation:
The run loop retains the display link. To remove the display link from all run loops, send an
invalidate()
message to the display link.
And a display link keeps strong reference to its target
. See invalidate()
documentation:
Removing the display link from all run loop modes causes it to be released by the run loop. The display link also releases the target.
So, you definitely have to invalidate()
. And if you're using self
as the target
of the display link, you cannot do this in deinit
(because the CADisplayLink
keeps a strong reference to its target).
A common pattern if doing this within a view controller is to set up the display link in viewDidAppear
and remove it in viewDidDisappear
.
For example:
private weak var displayLink: CADisplayLink?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
startDisplayLink()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
stopDisplayLink()
}
private func startDisplayLink() {
stopDisplayLink() // stop previous display link if one happens to be running
let link = CADisplayLink(target: self, selector: #selector(handle(displayLink:)))
link.add(to: .main, forMode: .commonModes)
displayLink = link
}
private func stopDisplayLink() {
displayLink?.invalidate()
}
@objc func handle(displayLink: CADisplayLink) {
// do something
}