Recently, as a consequence of working with the CGImageSource API, I found myself in a situation where I had hold of a CFNumber and wanted to get its value, as a CGFloat, in Swift.
CFNumber wraps numeric values in such a way that, to get the value out, you have to specify both the desired type, and provide a pointer to the memory of the variable that will hold the value. This kind of direct memory manipulation is not particularly suited to Swift’s priorities for type safety and memory protection. Here’s the API I’d need to use in Swift:
func CFNumberGetValue(_ number: CFNumber!, _ theType: CFNumberType, _ valuePtr: UnsafeMutableRawPointer!) -> Bool
The first two parameters are straightforward, but whenever I see types like “UnsafeMutableRawPointer” in Swift, my brain melts down a little. I have never really sat down to truly understand the nuanced differences between these types, so I usually just try something and hope it works. Here I am hoping for a gift from Swift’s implicit bridging:
// myCFNumber is 30.5 var myFloat: CGFloat = 0 CFNumberGetValue(myCFNumber, .floatType, &myFloat) print(myFloat) // "5.46688490824244e-315\n"
Welp. That didn’t work. Let’s see if we can refresh our memory about UnsafeMutableRawPointer. In the section titled “Raw, Unitialized Memory” or I read:
You can use methods like initializeMemory(as:from:) and moveInitializeMemory(as:from:count:) to bind raw memory to a type and initialize it with a value or series of values.
Oh jeez, am I really going to have to manually create an UnsafeMutableRawPointer? I’ll try anything:
var myFloat: CGFloat = 0 var myFloatPointer = UnsafeMutableRawPointer(mutating: &myFloat) CFNumberGetValue(myCFNumber, .floatType, myFloatPointer) print(myFloat) // "5.46688490824244e-315\n"
Alas, same problem. Surely somebody has figured this out? I try Googling for “CFNumberGetValue Swift GitHub” and find a promising result from an authoritative source. The Swift standard library itself!
var value: Float = 0 CFNumberGetValue(_cfObject, kCFNumberFloatType, &value)
Aha! Practically the same thing I was doing, except for one nuanced detail: the var value is declared as a Float instead of a CGFloat. But wait a minute, what file is this implementation in? NSNumber.swift? Oh, right. NSNumber and CFNumber are toll-free bridged, and Swift’s standard library fulfills that promise too:
let myFloat = (myCFNumber as NSNumber).floatValue print(myFloat) // 30.5
In fact, Swift’s Float type is even cozier with CFNumber than I expected. What started as a confused mission to make use of CFNumberGetValue and its unsafe pointer argument culminated in a bit of sample code from GitHub that ultimately led me to the understanding that the way to get a CFNumber’s value in Swift is … simply to ask for it:
let myFloat = Float(myCFNumber) print(myFloat) // 30.5