Swift: Interop in Beta 3
Compilers are Toddlers
Compilers are toddlers. You can't reason with them, you can't plead with them. They do not understand human emotions (yet) and live only in the moment. Any obstacle or failure, no matter how minor, is a complete disaster. When they expect something you fail to deliver, they complain loudly and often in front of others, creating much embarrassment.
Stop Rambling
With Xcode 6 Beta 3, we have an update to the Swift compiler and libraries so let's go about fixing some of the interop scenarios from previous posts. In most cases the changes are fairly minor, but the compiler will definitely complain if you don't put things just so.
The primary change is many instances of COpaquePointer
are now imported as UnsafePointer<()>
and const pointers as the corresponding ConstUnsafePointer<()>
, but there is no direct connection between UnsafePointer
and Unmanaged
so we simply need to convert by passing through COpaquePointer
where needed. I've created an extension that makes it a bit easier, but I chose not to create implicit conversion functions because I prefer to be explicit when working with pointers manually. This is a personal preference - you could certainly create the @conversion
functions if you wanted.
extension Unmanaged {
///Creates an UnsafePointer<()> to the object.
///Be sure you have taken the appropriate retain steps
///to keep the object alive
///before passing the pointer anywhere,
///or the object may be released and deallocated
///out from underneath you, leaving a dangling pointer!
func toUnsafePtr() -> UnsafePointer<()> {
return UnsafePointer<()>(self.toOpaque())
}
///Converts from an UnsafePointer<()> to an
///Unmanaged<T> object reference
///No retains/releases are performed, so be sure you
///manage object lifetimes properly.
///Passing an invalid pointer or a pointer of the wrong type is
///undefined behavior.
static func fromUnsafePtr(unsafe: UnsafePointer<()>) -> Unmanaged<T> {
return Unmanaged<T>.fromOpaque(COpaquePointer(unsafe))
}
}
Let's look an example:
let oldPtr = pthread_getspecific(key)
//old way
let unmanaged:Unmanaged<T> = Unmanaged.fromOpaque(oldPtr)
//new way
let unmanaged:Unmanaged<T> = Unmanaged.fromOpaque(COpaquePointer(oldPtr))
//using extension
let unmanaged = Unmanaged<T>.fromUnsafePtr(ptr)
Here the swift import of pthread_getspecific
changed; previously the return value was imported as a COpaquePointer
, but now comes in as an UnsafePointer<()>
. (If you haven't seen the <()>
generic type specifier before, ()
is the nil
type, so it's essentially saying the type parameter is unknown or unspecified)
Since there is no direct connection, we can create a new COpaquePointer
passing UnsafePointer<()>
to the initializer, then pass that to Unmanaged. We still have to declare our constant's type explicitly to give the generic type parameter.
With the extension, things are much cleaner; we just specify the type parameter on the right-hand side of the declaration and pass the UnsafePointer<()>
directly.
In C-based APIs that are concerned with letting you pass context around, you're probably working with void *
so the type of the pointer is unknown and you deal with UnsafePointer<()>
as opposed to something like UnsafePointer<NSString>
.
If you find yourself wanting to use a specified T
for UnsafePointer<T>
then you might re-evaluate what you're doing. The obvious exceptions to this are CoreFoundation
APIs where the pointer types are known but the type is not a bridged one. In that case, specifying T
makes perfect sense for correctness and safety.
Conclusion
Go forth and dangle COpaquePointer
in front of the compiler to buy yourself a few minutes more peace and quiet. At least until the next fit of LLVM ERROR: Broken function found, compilation aborted! strikes.
This blog represents my own personal opinion and is not endorsed by my employer.