Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When constructing a Swift UnsafeMutablePointer manually, is destroy/dealloc obligatory after alloc?

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?

like image 368
matt Avatar asked Jan 04 '15 17:01

matt


2 Answers

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.

like image 94
Airspeed Velocity Avatar answered Nov 11 '22 10:11

Airspeed Velocity


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) }
like image 34
pointum Avatar answered Nov 11 '22 09:11

pointum