How to Correctly Initialize an Unsafepointer in Swift

How to correctly initialize an UnsafePointer in Swift?

The simplest solution is using withUnsafePointer function:

let myFont = CTFontCreateWithName("Helvetica", 12, nil)
let myGlyph = CTFontGetGlyphWithName(myFont, "a")
var myTransform = CGAffineTransformIdentity

var path = withUnsafePointer(&myTransform) { (pointer: UnsafePointer<CGAffineTransform>) -> (CGPath) in
return CTFontCreatePathForGlyph(myFont, myGlyph, pointer)
}

The initialize is not a constructor. You would have to alloc a new memory using UnsafePointer<T>.alloc, then initialize and then dealloc. Function withUnsafePointer does that all for you.

Note that myTransform cannot be a constant (var not let) otherwise you cannot use it for an inout param (&myTransform).

What does `UnsafeMutablePointer.initialize()`actually do?

One reason you need initialize(), and the only one as for now maybe, is

for ARC.

You'd better think with local scope variables, when seeing how ARC works:

func test() {
var refVar: RefType = initValue //<-(1)
//...
refVar = newValue //<-(2)
//...
//<-(3) just before exiting the loacl scope
}

For a usual assignment as (2), Swift generates some code like this:

swift_retain(_newValue)
swift_release(_refVar)
_refVar = _newValue

(Assume _refVar and _newValue are unmanaged pseudo vars.)

Retain means incrementing the reference count by 1, and release means decrementing the reference count by 1.


But, think what happens when the initial value assignment as at (1).

If the usual assignment code was generated, the code might crash at this line:

swift_release(_refVar)

because newly allocated region for a var may be filled with garbages, so swift_release(_refVar) cannot be safely executed.

Filling the newly region with zero (null) and release safely ignoring the null could be one solution, but it's sort of redundant and not effective.

So, Swift generates this sort of code for initial value assignment:

(for already retained values, if you know ownership model, owned by you.)

_refVar = _initValue

(for unretained values, meaning you have no ownership yet.)

swift_retain(_initValue)
_refVar = _initValue

This is initialize.

No-releasing the garbage data, and assign an initial value, retaining it if needed.

(The above explanation of "usual assignment" is a little bit simplified, Swift omits swift_retain(_newValue) when not needed.)


When exiting the local scope at (3), Swift just generates this sort of code:

swift_release(_refVar)

So, this is deinitialize.


Of course, you know retaining and releasing are not needed for primitive types like Int, so initialize and deinitialize may be donothing for such types.

And when you define a value type which includes some reference type properties, Swift generates initialize and deinitialize procedures specialized for the type.


The local scope example works for the regions allocated on the stack, and initialize() and deinitialize() of UnsafeMutablePointer works for the regions allocated in the heap.

And Swift is evolving so swift, that you might find another reason for needing initialize() and deinitialize() in the future, you'd better make it a habit to initialize() and deinitialize() all allocated UnsafeMutablePointers of any Pointee types.

Initialization of 'UnsafePointerUInt8' results in a dangling pointer

Since Swift 3 it's possible to simply initialize a Data instance with an UInt8 array.

let sendBytes:[UInt8] = [0x0, 0x0, 0x5, 0x0]
let msgData = Data(sendBytes)

How to to assign the address of a local swift variable to an UnsafeMutablePointer

You're looking for withUnsafeMutablePointer(_: _:). It takes an inout parameter of type T and a block that takes a parameter of type UnsafeMutablePointer<T>. It is used as such:

var i: Int = 0
withUnsafeMutablePointer(&i) { (pointer) in
f(pointer)
}

Converting an UnsafePointer with length to a Swift Array type

You can simply initialize a Swift Array from an UnsafeBufferPointer:

func convert(length: Int, data: UnsafePointer<Int8>) -> [Int8] {

let buffer = UnsafeBufferPointer(start: data, count: length);
return Array(buffer)
}

This creates an array of the needed size and copies the data.

Or as a generic function:

func convert<T>(count: Int, data: UnsafePointer<T>) -> [T] {

let buffer = UnsafeBufferPointer(start: data, count: count);
return Array(buffer)
}

where length is the number of items that the pointer points to.

If you have a UInt8 pointer but want to create an [T] array from
the pointed-to data, then this is a possible solution:

// Swift 2:
func convert<T>(length: Int, data: UnsafePointer<UInt8>, _: T.Type) -> [T] {

let buffer = UnsafeBufferPointer<T>(start: UnsafePointer(data), count: length/strideof(T));
return Array(buffer)
}

// Swift 3:
func convert<T>(length: Int, data: UnsafePointer<UInt8>, _: T.Type) -> [T] {
let numItems = length/MemoryLayout<T>.stride
let buffer = data.withMemoryRebound(to: T.self, capacity: numItems) {
UnsafeBufferPointer(start: $0, count: numItems)
}
return Array(buffer)
}

where length now is the number of bytes. Example:

let arr  = convert(12, data: ptr, Float.self)

would create an array of 3 Floats from the 12 bytes pointed to by ptr.

How to Initialize OpaquePointer in Swift

What the compile error is telling you is that the initialiser that takes a bit pattern returns an optional OpaquePointer.

So you either should use it as an optional:

@State var OP: OpaquePointer? = OpaquePointer(bitPattern: 1)

Or if you're a 100% certain the opaque pointer is never nil, you can force unwrap it:

@State var OP: OpaquePointer = OpaquePointer(bitPattern: 1)!

However, if the OpaquePointer fails to initialise, your app will crash.

Please note that a bitPattern of 1 will most certainly fail, since this should point to an address in memory.

A better way to get an OpaquePointer is to derive it from an UnsafePointer or UnsafeMutablePointer, so you're sure the pointer is actually pointing to the thing you want it to.

What are you trying to point to? And do you really need an OpaquePointer? If so, I'd highly advise to not mess with pointers inside your SwiftUI view, do this stuff in a (or multiple) layers 'above' that (so either ViewModel or Model/Data).



Related Topics



Leave a reply



Submit