Let's say that in Swift I construct a C array manually and pass it, like this:
override func drawRect(rect: CGRect) {
let c = UIGraphicsGetCurrentContext()
var arr = UnsafeMutablePointer<CGPoint>.alloc(4)
arr[0] = CGPoint(x:0,y:0)
arr[1] = CGPoint(x:50,y:50)
arr[2] = CGPoint(x:50,y:50)
arr[3] = CGPoint(x:0,y:100)
CGContextStrokeLineSegments(c, arr, 4)
}
(I know I don't have to do that, but just bear with me.) If I don't call destroy
and/or dealloc
on this UnsafeMutablePointer, am I leaking the memory for four CGPoints?
The documentation for UnsafeMutablePointer
is pretty clear:
/// This type provides no automated
/// memory management, and therefore the user must take care to allocate
/// and free memory appropriately.
So if you allocate but don’t deallocate, you will leak memory. There’s no auto-deallocation on destruction of the pointer object.
Re whether you should destroy before deallocating, it’s also pretty clear:
/// The pointer can be in one of the following states:
///
/// - memory is not allocated (for example, pointer is null, or memory has
/// been deallocated previously);
///
/// - memory is allocated, but value has not been initialized;
///
/// - memory is allocated and value is initialized.
But bear in mind you can transition back and forth between these states. So after allocating then initializing then de-initialzing (aka destroying) the objects, the memory is no longer in the “initialized” state, so you can either re-initialize or deallocate it. You can also allocate, then deallocate, without ever initializing.
And when calling dealloc
:
/// Deallocate `num` objects.
...
/// Precondition: the memory is not initialized.
///
/// Postcondition: the memory has been deallocated.
Therefore you must call destroy on any initialized objects before calling dealloc
. You are probably right in that since something like CGPoint
is totally inert (just a struct of two floating point nums) it probably doesn’t do any harm not to call destroy
before you call dealloc
but you can’t be certain without knowing the implementation (of both the pointer struct and the compiler probably, since the standard lib is a quasi-part of the language there could be some baked-in optimizations), and generally, it’s just not a good habit to get into. Sooner or later you’ll forget to destroy a String
, and then you’ll be sorry.
(none of this accounts for the move
operations btw which combine initializing new memory with destroying old memory)
If you were hoping for some kind of automated self-cleanup of memory by UnsafePointer
, I don’t think this would be possible as a) it’s a struct, so can’t implement a deinit
to auto-deallocate when going out of scope, and b) it doesn’t track it’s own size – you have to track how much you allocate, and supply that back explicitly in the call to deallocate
.
There is something in the standard library that does auto-deallocate memory without you having to do it yourself – HeapBufferStorage
, the one and only class in the standard library. Presumably it’s a class specifically to benefit from an implementation of deinit
. There’s also HeapBuffer
to manage it with, and this has a handy isUniquelyReferenced()
function that allows you to tell if it’s been copied (even though it’s a struct) and so would allow you to implement copy-on-write capability similar to arrays and strings.
In Swift 2 UnsafeMutablePointer can be made a bit less frustrating by pairing alloc/dealloc with the new defer
keyword:
let ptr = UnsafeMutablePointer<T>.alloc(1)
defer { ptr.dealloc(1) }
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