Search code examples
swiftweak-referencescadisplaylink

How to set CADisplayLink in Swift with weak reference between target and CADisplayLink instance


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.


Solution

  • An better approach might be to invalidate the display link in viewWill/DidDisappear, see also

    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

    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.