I am implementing a pure Swift NSData alternative. Below is part of my Swift 2 code. As far as I know, Data instance deinitialization does not destroy() and dealloc() bytes the block buffer points to.
So, are there any ways of calling destroy and dealloc() on buffer pointers to prevent memory leaks before the Data instance deinitializes?
public struct Data: DataContainer {
public typealias Block = UInt8
public var blocks: UnsafeMutableBufferPointer<Block>
public init(bytes: UnsafeMutablePointer<Block>, length: Int) {
// copy bytes
let bytesCopy = UnsafeMutablePointer<Block>.alloc(length)
bytesCopy.initializeFrom(bytes, count: length)
// init byte blocks
self.blocks = UnsafeMutableBufferPointer<Block>(start: bytesCopy, count: length)
}
}
You can solve this by using a class for the actual implementation.
An example how this can be done is given in
Friday Q&A 2015-04-17: Let's Build Swift.Array:
Destruction can be solved by using a
class, which providesdeinit. The pointer can be destroyed there.classdoesn't have value semantics, but we can solve this by usingclassfor the implementation of thestruct, and exposing thestructas the external interface to the array. This looks something like:class ArrayImpl<T> { var ptr: UnsafeMutablePointer<T> deinit { ptr.destroy(...) ptr.dealloc(...) } } struct Array<T> { var impl: ArrayImpl<T> }You then write methods on
Arraythat forward to implementations onArrayImpl, where the real work is done.
Applying this approach to your problem could roughly look like this:
private class DataImpl {
typealias Block = UInt8
var blocks: UnsafeMutableBufferPointer<Block>
init(bytes: UnsafeMutablePointer<Block>, length: Int) {
// copy bytes
let bytesCopy = UnsafeMutablePointer<Block>.alloc(length)
bytesCopy.initializeFrom(bytes, count: length)
// init byte blocks
self.blocks = UnsafeMutableBufferPointer<Block>(start: bytesCopy, count: length)
}
deinit {
print("deinit")
blocks.baseAddress.destroy(blocks.count)
blocks.baseAddress.dealloc(blocks.count)
}
}
struct Data {
typealias Block = UInt8
private var impl : DataImpl
init(bytes: UnsafeMutablePointer<Block>, length: Int) {
impl = DataImpl(bytes: bytes, length: length)
}
}
A simple test shows that this works as expected, the data is released when the variable goes out of scope:
var bytes : [UInt8] = [1, 2, 3, 4]
do {
let data = Data(bytes: &bytes, length: bytes.count)
}
print("finished")
Output:
deinit finished
Thanks to @Martin R for his answer.
I've added class AutoreleasingMutableBufferPointer<T> as container and deallocator of struct UnsafeMutableBufferPointer<T>.
Here is the working code:
public class AutoreleasingMutableBufferPointer<T> {
public var buffer: UnsafeMutableBufferPointer<T>
public init(start pointer: UnsafeMutablePointer<T>, count length: Int) {
self.buffer = UnsafeMutableBufferPointer<T>(start: pointer, count: length)
}
public init(buffer: UnsafeMutableBufferPointer<T>) {
self.buffer = buffer
}
deinit {
self.buffer.baseAddress.destroy(buffer.count)
self.buffer.baseAddress.dealloc(buffer.count)
}
}
public struct Data: DataContainer {
public typealias Block = UInt8
public var blocks: AutoreleasingMutableBufferPointer<Block>
}
extension Data {
public init(bytes: UnsafeMutablePointer<Block>, length: Int) {
let bytesCopy = UnsafeMutablePointer<Block>.alloc(length)
bytesCopy.initializeFrom(bytes, count: length)
self.blocks = AutoreleasingMutableBufferPointer<Block>(start: bytesCopy, count: length)
}
}
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