简体   繁体   English

调用 VC deinit 后,Swift 闭包仍在内存中

[英]Swift closure is still in memory after VC deinit is called

I have a bluetooth class which passes when a char value is updated to a closure in a view controller (as well as the same closure in a singleton class).我有一个蓝牙类,当一个 char 值更新为视图控制器中的闭包(以及单例类中的相同闭包)时,它会通过。 when the VC deinit is called, the closure in the VC is still being executed when the char value is updated.当调用 VC deinit 时,在更新 char 值时仍在执行 VC 中的闭包。 I am using [weak self] for the closure in the VC.我在 VC 中使用 [weak self] 作为闭包。 I'd like to be able to stop this VC closure from being called when the view is deinitialised.我希望能够在取消初始化视图时阻止调用此 VC 闭包。 But I also don't understand why the other callback in the singleton is not being executed after the VC is presented!但是我也不明白为什么单例中的其他回调在VC呈现后没有被执行!

Included below is the syntax for the closure inside the VC下面包含了 VC 中闭包的语法

bluetooth.updatedCharacteristicsValue { [weak self] char in

[weak self] does not mean that the closure can be discarded, it only prevents the closure from retaining the VC (and therefore preventing the VC from being deinited). [weak self]并不意味着可以丢弃闭包,它只是防止闭包保留VC(从而防止VC被取消)。

Simply begin your closure with:简单地开始你的关闭:

guard let self = self else { return }

... to exit early if the VC no longer exists. ... 如果 VC 不再存在,则提前退出。

As for why the closure supplied by the VC is being called but the one in the singleton isn't, it sounds like your bluetooth class doesn't understand the concept of multiple 'users'.至于为什么 VC 提供的闭包被调用而单例中的闭包没有被调用,听起来您的蓝牙类不理解多个“用户”的概念。 Whoever registers their callback last is the one that is called.谁最后注册他们的回调就是被调用的那个。

An approach to handling your own observer registration with convenient self-unregistering tokens:一种使用方便的自注销令牌处理您自己的观察者注册的方法:

class ObserverToken {
    let id = UUID()
    private let onDeinit: (UUID) -> ()

    init(onDeinit: @escaping (UUID) -> ()) {
        self.onDeinit = onDeinit
    }

    deinit {
        onDeinit(id)
    }
}

class BluetoothThing {
    // Associate observers with the .id of the corresponding token
    private var observers = [UUID: (Int) -> ()]()

    func addObserver(using closure: @escaping (Int) -> ()) -> ObserverToken {
        // Create a token which sets the corresponding observer to nil
        // when it is deinit'd
        let token = ObserverToken { [weak self] in self?.observers[$0] = nil }
        observers[token.id] = closure
        return token
    }

    func tellObserversThatSomethingHappened(newValue: Int) {
        // However many observers we currently have, tell them all
        observers.values.forEach { $0(newValue) }
    }

    deinit {
        print("👋")
    }
}

// I've only made this var optional so that it can later be set to nil
// to prove there's no retain cycle with the tokens
var bluetooth: BluetoothThing? = BluetoothThing()

// For as long as this token exists, updates will cause this closure
// to be called. As soon as this token is set to nil, it's deinit
// will automatically deregister the closure
var observerA: ObserverToken? = bluetooth?.addObserver { newValue in
    print("Observer A saw: \(newValue)")
}

// Results in:
// Observer A saw: 42
bluetooth?.tellObserversThatSomethingHappened(newValue: 42)

// A second observer
var observerB: ObserverToken? = bluetooth?.addObserver { newValue in
    print("Observer B saw: \(newValue)")
}

// Results in:
// Observer A saw: 123
// Observer B saw: 123
bluetooth?.tellObserversThatSomethingHappened(newValue: 123)

// The first observer goes away.
observerA = nil

// Results in:
// Observer B saw: 99
bluetooth?.tellObserversThatSomethingHappened(newValue: 99)

// There is still one 'live' token. If it is retaining the
// Bluetooth object then this assignment won't allow the
// Bluetooth to deinit (no wavey hand)
bluetooth = nil

So if your VC stores it's token as a property, when the VC goes away, the token goes away and the closure is deregistered.因此,如果您的 VC 将其令牌存储为属性,则当 VC 消失时,令牌消失并且闭包将被取消注册。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM