In Objective-C, we can init CADisplayLink with Proxy Pattern to break strong reference:
WeakProxy *weakProxy = [WeakProxy weakProxyForObject:self];
self.displayLink = [CADisplayLink displayLinkWithTarget:weakProxy selector:@selector(displayDidRefresh:)];
Then, just invalidate the displayLink in dealloc
:
- (void)dealloc
{
[_displayLink invalidate];
}
However, NSProxy seems can't be inherited in Swift: https://bugs.swift.org/browse/SR-1715
I tried to write like this:
weak var weakSelf = self
displayLink = CADisplayLink(target: weakSelf!, selector: #selector(displayDidRefresh(dpLink:)))
It didn't work.
I would like to know if there is any way to achieve this like in Objective-C.
An better approach might be to invalidate the display link in
viewWill/DidDisappear
, see also
- Correct handling / cleanup / etc of CADisplayLink in Swift custom animation?
for useful information.
If that is not an option: Make the proxy object inherit from NSObject
instead of NSProxy
. An Objective-C solution is for example
given here
- CADisplayLink at iOS 6.0 not retaining target
and that can easily be translated to Swift 3:
class JAWeakProxy: NSObject {
weak var target: NSObjectProtocol?
init(target: NSObjectProtocol) {
self.target = target
super.init()
}
override func responds(to aSelector: Selector!) -> Bool {
return (target?.responds(to: aSelector) ?? false) || super.responds(to: aSelector)
}
override func forwardingTarget(for aSelector: Selector!) -> Any? {
return target
}
}
which can then be used as
displayLink = CADisplayLink(target: JAWeakProxy(target: self),
selector: #selector(didRefresh(dpLink:)))
Your approach
weak var weakSelf = self
displayLink = CADisplayLink(target: weakSelf!, selector: #selector(displayDidRefresh(dpLink:)))
does not work because it unwraps weakSelf
when the CADisplayLink
is initialized and passes a strong reference to self
as the target.
This proxy class should just work. Don't forget to invalidate before the dealloc.
import UIKit
class CADisplayLinkProxy {
var displaylink: CADisplayLink?
var handle: (() -> Void)?
init(handle: (() -> Void)?) {
self.handle = handle
displaylink = CADisplayLink(target: self, selector: #selector(updateHandle))
displaylink?.add(to: RunLoop.current, forMode: .commonModes)
}
@objc func updateHandle() {
handle?()
}
func invalidate() {
displaylink?.remove(from: RunLoop.current, forMode: .commonModes)
displaylink?.invalidate()
displaylink = nil
}
}
Usage:
var displaylinkProxy = CADisplayLinkProxy(handle: { [weak self] in
self?.updateTime()
})