Unsafe pointers in Swift: How to build a zombie (Part III, Xcode 6 Beta 7)


Working with unsafe pointers in Swift takes us back to a pre-ARC (automatic reference counting) world of retaining and releasing. This makes it very easy to create zombies and also memory leaks. Zombies occur where objects are over-released or where a deallocated object is accessed, while memory leaks occur when an object that has not been deallocated no longer has any pointers to it.

Working with regular Swift you won't have to worry about any of these factors, and even working with Cocoa methods that take an UnsafeMutablePointer the need shouldn't arise to go beyond the inout ampersand (&) syntax, which handles the seedy side of this world (see earlier post). However, my interest here is to help prepare the ground in case you should ever find the need to venture into this area of programming in Swift.

Leaks

A memory leak occurs (as stated) when an object no longer has a pointer pointing to it, but it has not been deallocated. This is very easy to achieve using the combination of an optional with an unsafe pointer in Swift
var c:UnsafeMutablePointer<String>!
c = UnsafeMutablePointer<String>.alloc(1)
c.initialize("Hello, world!")

c.memory  // returns "Hello, world!"

c = nil  // memory not deallocated, we can't now release object
and the above code is an example of this.

The right thing to do

If we don't deallocate memory from an unsafe pointer for which we allocated memory the effects might not be as dramatic as a crash, but it is our responsibility to make sure opportunities for leaks do not arise. So instead what I should have done is this:
var c:UnsafeMutablePointer<String>!
c = UnsafeMutablePointer<String>.alloc(1)
c.initialize("Hello, world!")

c.memory  // returns "Hello, world!"

c.destroy() // object placed back into uninitialized state
c.dealloc(1) // memory deallocated

c = nil // now safe to set optional to nil

Attack of the Zombie

As per the earlier definition, a zombie is an over-released object or a deallocated object being accessed. The following is an example of a zombie crashing your app:
var c:UnsafeMutablePointer<String>!
c = UnsafeMutablePointer<String>.alloc(1)
c.initialize("Hello, world!")

c.memory  // returns "Hello, world!"

c.destroy() // object placed back into uninitialized state
c.dealloc(1) // memory deallocated
c.dealloc(1) // memory deallocated again - CRASH!!

c = nil // now safe to set optional to nil
This instance would be easy to spot, but if you are working in more complex ways the same might not be true. It would be true, however, that you'd likewise experience a crash when you over-released the object.

The owner's responsibility

Let's consider now a situation where we have a second variable which takes on the value of our optional UnsafeMutablePointer:
var c:UnsafeMutablePointer<String>!
c = UnsafeMutablePointer<String>.alloc(1)
c.initialize("Hello, world!")

var d = c // d takes on the value of c
    
c.destroy()
c.dealloc(1)

c = nil 
d = nil // no need to destroy and dealloc d, if we did then we'd have a zombie crash!
Here we're not bothering to destroy or dealloc the variable that takes on the value of the first because it isn't the creator of the object, and so it isn't responsible for its deallocation. The responsibility lies with the first variable alone and in fact if we used dealloc on the second variable we'd see a crash.

What if we create an unsafe pointer by calling a function?

If you call a function and an unsafe pointer is returned, then the rule is you didn't create the pointer and so it isn't your responsibility to free() or dealloc(). This is unless you are explicitly told to do so in the documentation.

So for example this should cause no issues:
var x:UnsafeMutablePointer<tm>!
var t = time_t()
time(&t)
x = localtime(&t)
x = nil
Note: if an unsafe pointer is returned from a C function and does require releasing, we'd need to write free(x) rather than x.destroy() and x.dealloc() (see Apple Dev Forum discussion referenced below).

Further reading

Memory Management Policy (Apple) - pre-Swift, but very helpful in understanding release responsibilities

Apple Dev Forum Discussion ('Is it our responsibility to destroy and dealloc UnsafePointers returned from C functions?')

Thanks to contributors at the Apple Dev Forum and also to those contributing to an understanding on StackOverflow.
Endorse on Coderwall

Comments