In Swift (I'm using 4.1), is there a way to do some clean-up in an extension when an object is being destructed? I have in mind the kind of code that would go in deinit()
, but an extension can't declare deinit()
. (Besides which, if several extensions needed to do this, there would be multiple deinit()
declarations.)
Creating an extension in Swift Creating extensions is similar to creating named types in Swift. When creating an extension, you add the word extension before the name. extension SomeNamedType { // Extending SomeNamedType, and adding new // functionality to it. }
A Swift extension allows you to add functionality to a type, a class, a struct, an enum, or a protocol.
Extensions let us add functionality to classes, structs, and more, which is helpful for modifying types we don't own – types that were written by Apple or someone else, for example.
Protocols let you describe what methods something should have, but don't provide the code inside. Extensions let you provide the code inside your methods, but only affect one data type – you can't add the method to lots of types at the same time.
I did not find a way to exactly get what you want, but maybe this code will help. I have never tried it, so maybe use it more as an inspiration. In a nutshell, it allows you to add bits of code that will excute on deinitialization.
/// 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)
}
}
Now let's assume you have your object, let's call it A
and you want to create some code inside an extension, you can use onDeinit
to add your clean-up code like this:
extension A {
func someSetup() {
// Do your thing...
onDeinit {
/// This will be executed when A is deinitialized
print("Execute this")
}
}
}
You can also add it from outside the extension,
let a = A()
a.onDeinit {
print("Deinitialized")
}
deinit
) you need to call one. But anyway in protocol extensions, you can never really use the underlying object's life cycle, so it should be okay for most cases.A
's deinit
and the DeinitializationObserver
's deinit
. You might need to drop the assumption that A
is still in memory when you write the code inside the closure.onDeinit
you can update the associated object to retain an array of DeinitializationObserver
Lifetime
for this kind of things. However, it is more complicated than what I wrote here. Instead, it looks like they swizzle the dealloc
selector. If you're interested you can take a look inside - https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoa/NSObject+Lifetime.swift
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With