Can you elaborate a bit on this?
The gotcha relates to pointer identity. I’m going to explain this in terms of atomics, because that’s super easy to understand, but the same problem crops up when you use low-level C APIs.
Imagine you have a C function like this:
void incrementAtomic(uint32_t * counter);
This gets imported into Swift as:
func incrementAtomic(_ counter: UnsafeMutablePointer<UInt32>!)
You might then think about wrapping that in a Swift class:
class MyAtomicCounter {
private var counter: UInt32 = 0
func increment() {
incrementAtomic(&self.counter)
}
}
This is not safe. The reason its not safe is that Swift’s &
operator, as used on line 6, is not the same as C’s. Conceptually, the Swift version looks like the following:
var tmp = self.counter
incrementAtomic(&tmp)
self.counter = tmp
In many circumstances that temporary variable gets optimised away, and thus things work, but that’s not guaranteed. To guarantee that this works, you have to manage the memory yourself, so something like this:
class MyAtomicCounter {
init() {
self.lockPtr = UnsafeMutablePointer<UInt32>.allocate(capacity: 1)
}
deinit {
self.lockPtr.deallocate()
}
private var lockPtr: UnsafeMutablePointer<UInt32>
func increment() {
incrementAtomic(self.lockPtr)
}
}
It’s not that this is hard code to write, it’s just a gotcha you have to be aware of. As you’ll know from my other posts, I’m a big fan of using high-level abstractions by default, and only reverting to low-level constructs if I absolutely need to. Hence my recommendation to use NSLock
.
Share and Enjoy
Quinn “The Eskimo!” @ DTS @ Apple