Archivio mensile:Luglio 2014

Swift and pointers, news in Beta 3 and 4

In this post: https://www.lombax.it/?p=302 you have learned how to play with pointers in Swift.
Things have changed in latest betas, and more researches came in a better, shorter and more concise code. In short:

– CMutablePointer has been removed
– Code like this:

// swift side
 var opaque = COpaquePointer.null() // create a new opaque pointer pointing to null
 TestClass.writeString(&opaque)
 var string = Unmanaged.fromOpaque(opaque).takeRetainedValue()
 println(string)
 // this prints pippo pluto

has been revealed to be not safe:
There are circumstances in which Swift does not give you the original address of the variable, but instead gives you an address of a temporary variable that will be assigned back to the original once the call is over. That means you can’t count on the address of opaque being valid after the call to TestClass.writeString(&opaque). So, starting at the line ” var string = Unmanaged.fromOpaque(opaque).takeRetainedValue()”, the code can fail

Now, let’s say that we want to create a C/Objc function that takes all kind of objects and values (simply, it can receive a void** pointer), and pass a value back to Swift.
In this example we will pass in an NSString, changing its value in Objc and then reading it back in Swift. There are better methods to do this kind of things with NSString, passing a void** is used mainly to manage buffers, but in the example we’ll use an NSString for leaving things simple.

The Objc method is this

// with this objc function I completely replace the pointer
 + (void)writeString:(void **)var
 {
     NSString *oldString = CFBridgingRelease(*var); // since we are transferring opaque pointers, ARC can't help us. So, we have to release the old string before replacing it, otherwise it will leak 
     NSString *aString = [[NSString alloc] initWithFormat:@"pippo %@", @"pluto"];
     *var = (void *)CFBridgingRetain(aString); // Since aString will be released by ARC when the method ends, we need to send an additional retain. This retain is balanced by ARC on the Swift side
 }

Now, on the Swift part, the code is more simple than the previous version:

var aString : NSString = NSString(string: "test") // create the string
withUnsafePointer(&aString) { (arg: UnsafePointer) -> () in
     var aPtr = reinterpretCast(arg) as UnsafePointer<UnsafePointer<()>>
     TestMe.writeString(aPtr)
}
println(aString) // this will print "pippo pluto"

Some things to know

1) withUnsafePointer is a function that takes: an inout parameter (&parameterName) and a closure, where an arg (of type UnsafePointer) is passed in.
It can be written even in this way

withUnsafePointer(&aString, { … closure ….})

However, Swift give you a convenient way to put the closure after the parentheses. For this, look at the “Trailing Closures” chapter in the Swift Programming Language Book

2) reinterpretCast() permits to easily change the type of a pointer to another. Since Swift types are all reference types, and we want to obtain the void** pointer:
– when we pass in &aString we obtain a pointer to a Swift Reference type to an NSString, so a pointer to a pointer, and so a void**. We need only to make it “more generic”, and we to this casting to UnsafePointer<UnsafePointer<()>>
() is a TypeAlias to Void, so aPtr will be a pointer to a void pointer