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

元气小坏坏 提交于 2019-12-31 02:43:08

问题


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.


回答1:


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.




回答2:


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()
                })


来源:https://stackoverflow.com/questions/44096793/how-to-set-cadisplaylink-in-swift-with-weak-reference-between-target-and-cadispl

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!