Unsafemutablepointer in Swift as Replacement for Properly Sized C Array in Obj-C

UnsafeMutablePointer in swift as replacement for properly sized C Array in Obj-C

Normally you can just pass an array of the required type as an in-out parameter, aka

var coords: [CLLocationCoordinate2D] = []
polyline.getCoordinates(&coords, range: NSMakeRange(0, polyline.pointCount))

but that documentation makes it seem like a bad idea! Luckily, UnsafeMutablePointer provides a static alloc(num: Int) method, so you can call getCoordinates() like this:

var coordsPointer = UnsafeMutablePointer<CLLocationCoordinate2D>.alloc(polyline.pointCount)
polyline.getCoordinates(coordsPointer, range: NSMakeRange(0, polyline.pointCount))

To get the actual CLLocationCoordinate2D objects out of the mutable pointer, you should be able to just loop through:

var coords: [CLLocationCoordinate2D] = []
for i in 0..<polyline.pointCount {
coords.append(coordsPointer[i])
}

And since you don't want a memory leak, finish up like so:

coordsPointer.dealloc(polyline.pointCount)

Just remembered Array has a reserveCapacity() instance method, so a much simpler (and probably safer) version of this would be:

var coords: [CLLocationCoordinate2D] = []
coords.reserveCapacity(polyline.pointCount)
polyline.getCoordinates(&coords, range: NSMakeRange(0, polyline.pointCount))

Testcase failed after converting codes from Objective-C to Swift

Objective-C version of numberOfTrailingZeros:

// Ported from OpenJDK Integer.numberOfTrailingZeros implementation
- (int32_t)numberOfTrailingZeros:(int32_t)i {
int32_t y;
if (i == 0) return 32;
int32_t n = 31;
y = i <<16; if (y != 0) { n = n -16; i = y; }
y = i << 8; if (y != 0) { n = n - 8; i = y; }
y = i << 4; if (y != 0) { n = n - 4; i = y; }
y = i << 2; if (y != 0) { n = n - 2; i = y; }
return n - (int32_t)((uint32_t)(i << 1) >> 31);
}

When translating numberOfTrailingZeros, you changed the return value from Int32 to Int. That is fine, but the last line of the function is not operating properly as you translated it.

In numberOfTrailingZeros, replace this:

return n - Int((UInt((i << 1)) >> 31))

With this:

return n - Int(UInt32(bitPattern: i << 1) >> 31)

The cast to UInt32 removes all but the lower 32 bits. Since you were casting to UInt, you weren't removing those bits. It is necessary to use bitPattern to make this happen.

Converting a String to UnsafeMutablePointer UInt16

You have to create an array with the UTF-16 representation of the Swift
string that you can pass to the function, and on return create
a Swift string from the UTF-16 array result.

Lets assume for simplicity that the C function is imported to Swift as

func translateString(_ source: UnsafeMutablePointer<UInt16>, _ sourceLen: UnsafeMutablePointer<CInt>,
_ dest: UnsafeMutablePointer<UInt16>, _ destLen: UnsafeMutablePointer<CInt>)

Then the following should work (explanations inline):

// Create array with UTF-16 representation of source string:
let sourceString = "Hello world"
var sourceUTF16 = Array(sourceString.utf16)
var sourceLength = CInt(sourceUTF16.count)

// Allocate array for UTF-16 representation of destination string:
let maxBufferSize = 1000
var destUTF16 = Array<UInt16>(repeating: 0, count: maxBufferSize)
var destLength = CInt(destUTF16.count)

// Call translation function:
translateString(&sourceUTF16, &sourceLength, &destUTF16, &destLength)

// Create Swift string from UTF-16 representation in destination buffer:
let destString = String(utf16CodeUnits: destUTF16, count: Int(destLength))

I have assumed that the C function updates destLength to reflect
the actual length of the translated string on return.

How to get bytes out of an UnsafeMutableRawPointer?

load<T> reads raw bytes from memory and constructs a value of type T:

let ptr = ... // Unsafe[Mutable]RawPointer
let i16 = ptr.load(as: UInt16.self)

optionally at a byte offset:

let i16 = ptr.load(fromByteOffset: 4, as: UInt16.self)

There is also assumingMemoryBound() which converts from a Unsafe[Mutable]RawPointer to a Unsafe[Mutable]Pointer<T>, assuming that the pointed-to memory contains a value of type T:

let i16 = ptr.assumingMemoryBound(to: UInt16.self).pointee

For an array of values you can create a "buffer pointer":

let i16bufptr = UnsafeBufferPointer(start: ptr.assumingMemoryBound(to: UInt16.self), count: count)

A buffer pointer might already be sufficient for your purpose, it
is subscriptable and can be enumerated similarly to an array.
If necessary, create an array from the buffer pointer:

let i16array = Array(i16bufptr)

As @Hamish said, more information and details can be found at

  • SE-0107 UnsafeRawPointer API

Converting C char array (unsafe pointer) to String

You seem unwilling to show your code, so I can't really help. But this looks just wrong:

let deviceName = UnsafeMutablePointer<Character>.alloc(64)
/* other statements in which deviceName is filled */

That is not how to get hold of a C string in Swift, and if you believe that it is a C string in Swift, you're wrong; it would need to be an array of Int8 (C characters), not Swift Character (a struct!), to be a C string.

In other words, C char (your question's title) is not Swift Character - they are nothing like one another. A C char is a small number, a Swift Character is an object in an object-oriented language that C knows nothing about!



Related Topics



Leave a reply



Submit