在对象deinit上调用扩展中的代码?

Ted*_*opp 3 memory-leaks swift deinit

在Swift中(我使用的是4.1),当一个对象被破坏时,有没有办法在扩展中做一些清理工作?我想到了可以进入的代码类型deinit(),但扩展无法声明deinit().(除此之外,如果需要多个扩展,则会有多个deinit()声明.)

Jul*_*oud 7

我没有找到一种方法来准确得到你想要的东西,但也许这段代码会有所帮助.我从来没有尝试过,所以也许更多地使用它作为灵感.简而言之,它允许您添加将在取消初始化时执行的代码.

/// This is a simple object whose job is to execute
/// some closure when it deinitializes
class DeinitializationObserver {

    let execute: () -> ()

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

    deinit {
        execute()
    }
}

/// We're using objc associated objects to have this `DeinitializationObserver`
/// stored inside the protocol extension
private struct AssociatedKeys {
    static var DeinitializationObserver = "DeinitializationObserver"
}

/// Protocol for any object that implements this logic
protocol ObservableDeinitialization: AnyObject {

    func onDeinit(_ execute: @escaping () -> ())

}

extension ObservableDeinitialization {

    /// This stores the `DeinitializationObserver`. It's fileprivate so you
    /// cannot interfere with this outside. Also we're using a strong retain
    /// which will ensure that the `DeinitializationObserver` is deinitialized
    /// at the same time as your object.
    fileprivate var deinitializationObserver: DeinitializationObserver {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.DeinitializationObserver) as! DeinitializationObserver
        }
        set {
            objc_setAssociatedObject(
                self,
                &AssociatedKeys.DeinitializationObserver,
                newValue,
                objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC
            )    
        }
    }

    /// This is what you call to add a block that should execute on `deinit`
    func onDeinit(_ execute: @escaping () -> ()) {
        deinitializationObserver = DeinitializationObserver(execute: execute)
    }
}
Run Code Online (Sandbox Code Playgroud)

这个怎么运作

现在让我们假设您有对象,让我们调用它,A并且您想在扩展中创建一些代码,您可以使用onDeinit这样添加清理代码:

extension A {

    func someSetup() {

        // Do your thing...


        onDeinit {
            /// This will be executed when A is deinitialized
            print("Execute this")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

您也可以从扩展程序外添加它,

let a = A()
a.onDeinit {
    print("Deinitialized")
}
Run Code Online (Sandbox Code Playgroud)

讨论

  • 这与您想要的不同,而不是定义一个函数(deinit),您需要调用它.但无论如何,在协议扩展中,你永远不能真正使用底层对象的生命周期,因此对大多数情况来说应该没问题.
  • 我不确定A's deinitDeinitializationObserver's 之间的执行顺序deinit.A在闭包内编写代码时,可能需要删除仍在内存中的假设.
  • 如果您需要多个onDeinit,则可以更新关联对象以保留数组DeinitializationObserver
  • 在我的代码中,我通常使用ReactiveCocoa来做Lifetime这种事情.但是,它比我在这里写的更复杂.相反,它们看起来像是在调整dealloc选择器.如果你有兴趣可以看看里面 - https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoa/NSObject+Lifetime.swift

  • 这绝对很棒,工作和天才。一方面尽量不要在任何地方滥用它以免弄乱你的代码,但在某些地方它非常好。例如,如果您在扩展中订阅通知,您显然想在 deinit 上取消订阅;否则你有不好的访问。五颗星给你的朋友! (2认同)