Let's say I have some data structure S
that allocates some memory to a pointer. It's a struct because I want to avoid double indirection when accessing the data pointed to by its pointer, ie:
struct S {
var somePtr: UnsafeMutableRawPointer
init() {
somePtr = UnsafeMutableRawPointer.allocate(byteCount: 1024, alignment: 16)
print("Allocated", somePtr)
}
func doSomething() { print("Does something") }
func deallocate() {
print("Dellocating", somePtr)
somePtr.deallocate()
}
}
When using S
, I have to remember to call deallocate()
:
let s = S()
s.doSomething()
s.deallocate()
or:
let s = S()
defer { s.deallocate() }
s.doSomething()
Now, and here's the thing I'm wondering about, AFAICS the deallocation can be done implicitly at the end of an S
-instance's lifetime, like so:
struct S {
var somePtr: UnsafeMutableRawPointer
private final class Deinitializer {
var block: () -> Void
init(_ block: @escaping () -> Void) { self.block = block }
deinit { block() }
}
private let deinitializer: Deinitializer
init() {
somePtr = UnsafeMutableRawPointer.allocate(byteCount: 1024, alignment: 16)
print("Allocated", somePtr)
deinitializer = Deinitializer { [somePtr] in
print("Dellocating", somePtr)
somePtr.deallocate()
}
}
func doSomething() { print("Does something") }
}
func test() {
let s = S()
s.doSomething()
}
test()
Is this a reasonable thing to do for types like S
?
The only downsides I can see are:
- It adds 8 bytes of storage to the struct.
- It has to allocate and deallocate the class instance, and call deallocate via indirection. This is only a problem if
S
instances are created and destroyed with high frequency, in which case allocating and deallocatingsomePtr
would already be a problem. - No explicit control over deallocation, which seems like it's not a big deal, because the same control is still available via
do { ... }
.
Are there more?