微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

当回调数组不再有调用者时,如何自动从回调数组中删除回调?

如何解决当回调数组不再有调用者时,如何自动从回调数组中删除回调?

我有这些课程:

class Callback {
    let callback: () -> Void
    init(callback: @escaping () -> Void) {
        self.callback = callback
    }
}

class CallbackContainer {
    private var callBacks = [Callback]()
    
    func add(callback: @escaping () -> Void) -> Callback {
        let cl = Callback(callback: callback)
        callBacks.append(cl)
        return cl
    }
    
    func callAll() {
        for callback in callBacks {
            callback.callback()
        }
    }
}

class Container {
    let callbackContainer = CallbackContainer()
    
    func executeSomeLongTasks() {
        dispatchQueue.main.asyncAfter(deadline: .Now() + 5) {[weak self] in
            self?.callbackContainer.callAll()
        }
    }
}

class AViewController: UIViewController {
    var callback: Callback?
    let container: CallbackContainer
    init(container: CallbackContainer) {
        self.container = container
        super.init(nibName: nil,bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        callback = container.add {
            debugPrint("Called callback on A")
        }
    }
    
    deinit {
        debugPrint("Deinited AViewController")
    }
}

我希望 CallbackContainer 在没有人再调用自动删除回调。例如:

let container = Container()

var aVC: AViewController? = AViewController(container: container.callbackContainer)

//on somewhere executed long task
container.executeSomeLongTasks()
aVC = nil
// should not call callback here

这里,当 aVC 被释放时 - 比如 aVC = nil 或从导航堆栈中弹出 - 那么存储在 callbackContainer 中的回调(在 AViewController viewDidload() 上创建),应该被删除自动从内部数组中删除,无需在 AViewController deinit() 上手动删除它。因此,不会再从 CallbackContainer.callAll()

调用回调

它应该像 disposeBag 上的 RxSwift 一样工作。我看到了 disposeBag 代码,但不明白。

有人知道吗?

解决方法

你需要的是一种弱数组。据我所知,Swift 基础类中没有这样的类型:我的第一个想法是使用 NSMapTable 或类似 weakToStrongObjects 配置的东西,它被记录为将键存储为弱引用,但不幸的是,该类不会自动重新组织,请参阅 discussion part:

不推荐使用从弱到强的映射表。被清零的弱键 > 的强值将继续保持,直到映射表调整自身大小。

你可以做的是:

  • 创建一个存储弱引用和闭包的装箱对象
  • 将该框添加到 callBacks 数组中。
  • 调用 callAll 时,首先清理 callBacks 数组以移除所有带有 nil 引用的框。

手动清理是一个缺点,因为当弱引用由 ARC 引导时,您不会收到通知。但它是由Container集中完成的,所以客户端不需要关心。

例如:

nil

然后,在 typealias CallbackType = () -> Void class WeakCallback : Equatable { let id = UUID() weak var refItem: AnyObject? let callback:CallbackType init(refItem: AnyObject,callback:@escaping CallbackType) { self.refItem = refItem self.callback = callback } static func == (lhs: WeakCallback,rhs: WeakCallback) -> Bool { return lhs.id == rhs.id } } class CallbackContainer { var callBacks = [WeakCallback]() func add(ref:AnyObject,callback: @escaping () -> Void) -> WeakCallback { cleanupCallbackArray() let cl = WeakCallback(refItem:ref,callback: callback) callBacks.append(cl) return cl } func callAll() { cleanupCallbackArray() for cb in callBacks { if (cb.refItem != nil) { cb.callback() } else { print ("oops,found nil for \(cb.id)") } } } func cleanupCallbackArray() { callBacks = callBacks.filter { cb -> Bool in if (cb.refItem == nil) { print ("\(cb.id) has nil reference,will be removed") return false } return true } } } 中,添加视图控制器作为对容器的引用:

viewDidLoad

测试:

    override func viewDidLoad() {
        super.viewDidLoad()
        callback = container.add (ref:self) {
            debugPrint("Called callback on A")
        }
    }

给出:

callAll 1

“在 A 上调用回调”

设置为零

“定义的 AViewController”

callAll 2

81C3D375-4B0E-4806-8088-AFE81C3125F3 没有参考,将被删除

如您所见,不会在“callAll 2”时调用闭包。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。