Learn to use uncooked pointer references, work together with unsafe pointers and manually handle reminiscence addresses in Swift.
Swift
Pointers in Swift
What’s is a pointer? A pointer is a variable that shops the reminiscence handle of a referenced object. As I discussed this in my earlier article, concerning the reminiscence format of varied objects in Swift, a reminiscence handle is only a hexadecimal illustration of an information situated someplace within the reminiscence. You utilize situations of varied unsafe pointer varieties to entry information of a particular kind in reminiscence.
Why will we need to use these sort of pointers on the first place? By default you do not have to jot down unsafe Swift code, and in a lot of the instances you may stay with out unsafe reminiscence pointers. These pointers come useful if it’s important to interoperate with different “unsafe” languages, equivalent to C. There are different low degree or legacy APIs that require the usage of handbook reminiscence administration, however you should not be afraid, when you get aware of unsafe Swift pointer varieties you may know much more about how reminiscence works and you will see how skinny is the layer between C libraries and Swift. 😱
What sort of pointers are there? With a purpose to perceive pointer varieties higher in Swift, let’s get again to C only for a second. Take into account the next C code instance:
int principal () {
int x = 20;
int* xPointer = &x;
printf("x handle: `%p`n", &x);
printf("x worth: `%u`n", x);
printf("pointer handle: `%p`n", &xPointer);
printf("pointer reference: `%p`n", xPointer); // equals the handle of x
printf("pointer reference worth: `%u`n", *xPointer);
*xPointer = 420;
printf("x worth: `%u`n", x);
printf("pointer reference worth: `%u`n", *xPointer);
x = 69;
printf("x worth: `%u`n", x);
printf("pointer reference worth: `%u`n", *xPointer);
return 0;
}
It can save you this code snippet utilizing the principal.c
title, then compile & run it utilizing the clang principal.c -o principal && ./principal
command. It can present a fairly similiar output.
$ clang principal.c -o principal && ./principal
x handle: `0x16b0c7a38`
x worth: `20`
pointer handle: `0x16b0c7a30`
pointer reference: `0x16b0c7a38`
pointer reference worth: `20`
pointer worth `20`
[email protected]~: clang principal.c -o principal && ./principal
x handle: `0x16d52fa38`
x worth: `20`
pointer handle: `0x16d52fa30`
pointer reference: `0x16d52fa38`
pointer reference worth: `20`
x worth: `420`
pointer reference worth: `420`
x worth: `69`
pointer reference worth: `69`
So what is going on on right here? Effectively, we merely created an integer variable and a pointer variable with an integer kind. We used the handle of our x variable (&x) to affiliate our pointer with the reminiscence handle of x. Now each variables factors to the identical reminiscence handle.
We will affirm this by logging the reminiscence handle of each variables. We will additionally alter the worth of x by updating the referenced worth of the pointer (we will use the * character for this) or go along with the same old make x = one thing line. We have merely logged the modified values to verify that the pointer worth replace additionally modified the worth of x. Lets say that xPointer is only a reference to x.
Now how will we obtain the identical factor in Swift? First we have now to learn to outline a pointer kind. This is a fast listing of all the unsafe pointer objects accessible within the Swift normal library:
You might need seen a sample right here: Unsafe|[Mutable][Raw][Buffer]Pointer[<T>]
.
Unsafe pointers are simply direct reminiscence addresses. All the pieces that’s mutable could be modified, in different phrases you may write to that handle. Uncooked signifies that there is no such thing as a related (generic, T) kind to the given pointer, it is only a blob of uncooked bytes. Buffers are batches (collections) of pointers.
Don’t fret if these varieties are fairly complicated for you proper now, it’s going to all make sense in a couple of minutes. Let’s get again to our authentic C pattern code and port it to Swift actual fast.
var x: Int = 20
var xPointer: UnsafeMutablePointer<Int> = .init(&x)
print("x handle:", UnsafeRawPointer(&x));
print("x worth:", x);
print("pointer handle:", UnsafeRawPointer(&xPointer));
print("pointer reference:", xPointer);
print("pointer reference worth:", xPointer.pointee);
xPointer.pointee = 420;
print("x worth:", x);
print("pointer reference worth:", xPointer.pointee);
x = 69;
print("x worth:", x);
print("pointer reference worth:", xPointer.pointee);
We have created an UnsafeMutablePointer<Int>
reference to our x worth, that is principally an int*
kind if we return to the C instance. We will use the identical ampersand (&) character to create pointers from variables. We have created a typed mutable pointer, since we would like to vary the worth of the referenced integer object (via the pointee property) in a while.
To print the reminiscence handle of a variable we will merely use an UnsafeRawPointer
kind, as a result of we do not actually care concerning the underlying “pointee” worth, however we simply want the handle of the reference. In the event you print a pointer kind the debug description will include the underlying reminiscence handle of the referenced object. On this case the handle of x
and xPointer
. 🤔
Working with typed pointers in Swift
How can we retailer some values at “unsafe” reminiscence addresses in Swift? The most straightforward means is that we begin with a generic mutable pointer. We will allocate pointers utilizing the required capability, since we’re working with unsafe reminiscence, we additionally must deallocate reminiscence after we have completed utilizing it. We additionally must manually initialize pointer reference values, unsafe pointers can already include some type of leftover information, so the secure strategy is to initialize them with a brand new default worth.
let numbers = [4, 8, 15, 16, 23, 42]
let pointer = UnsafeMutablePointer<Int>.allocate(capability: numbers.rely)
pointer.initialize(repeating: 0, rely: numbers.rely)
defer {
pointer.deinitialize(rely: numbers.rely)
pointer.deallocate()
}
for (index, worth) in numbers.enumerated() {
pointer.superior(by: index).pointee = worth
}
print(pointer.superior(by: 5).pointee);
let bufferPointer = UnsafeBufferPointer(begin: pointer, rely: numbers.rely)
for (index, worth) in bufferPointer.enumerated() {
print(index, "-", worth)
}
let bufferPointer = UnsafeMutableBufferPointer(begin: pointer, rely: numbers.rely)
for (index, _) in bufferPointer.enumerated() {
bufferPointer[index] = index + 1
}
After we have now the allotted reminiscence storage, we will set the suitable pointee values, since we have allotted the pointer with a capability of six integer values, we will retailer as much as 6 numbers utilizing this pointer. You should use the superior(by:)
methodology (pointer arithmetic (pointer + 5).pointee = 42)
works as nicely) to maneuver to the following handle and set the pointee worth of it.
The very final thing I might prefer to let you realize is that you should use a typed buffer pointer to iterate via these quantity references. You may consider buffer pointers as an array of pointer references. It’s doable to enumerate via pointer values and indexes straight. You may replace buffer pointer values by utilizing the subscript syntax on a mutable buffer pointer. 💡
We already talked concerning the UnsafePointer
, UnsafeMutablePointer
, UnsafeRawPointer
, UnsafeBufferPointer
and UnsafeMutableBufferPointer
kind let’s dive in to uncooked pointers.
Reminiscence administration utilizing uncooked pointers
Typed pointers present some sort of security if it involves pointers, however how will we work with uncooked pointers? We have already seen how straightforward is to print out an handle of a given worth kind utilizing an UnsafeRawPointer
reference, now it is time to join the dots and allocate some unsafe uncooked reminiscence. If it’s worthwhile to know extra about reminiscence format in Swift, please learn my earlier article.
To begin with, we’ll have to understand how a lot reminiscence to allocate. We will use the MemoryLayout struct to get information a few worth kind. We will use the stride and the variety of gadgets to rely how a lot byte is required to retailer our information kind utilizing a uncooked reminiscence storage.
let numbers = [4, 8, 15, 16, 23, 42]
let stride = MemoryLayout<Int>.stride
let alignment = MemoryLayout<Int>.alignment
let byteCount = stride * numbers.rely
let pointer = UnsafeMutableRawPointer.allocate(byteCount: byteCount, alignment: alignment)
defer {
pointer.deallocate()
}
for (index, worth) in numbers.enumerated() {
pointer.superior(by: stride * index).storeBytes(of: worth, as: Int.self)
}
let bufferPointer = UnsafeRawBufferPointer(begin: pointer, rely: byteCount)
for index in 0..<numbers.rely {
let worth = bufferPointer.load(fromByteOffset: stride * index, as: Int.self)
print(index, "-", worth)
}
After we have allotted the required house, we will use the pointer and the superior(by:)
methodology to retailer byte values of a given kind (storeBytes(of:as:)
) as uncooked bytes. We will load a given kind utilizing the load(as:)
methodology. It’s price to say that if the reminiscence doesn’t include a price that may be represented because the given kind, incompatible worth varieties can crash your app. ☠️
Anyway, in the event you saved a number of values utilizing a pointer you should use the uncooked buffer assortment to iterate via them and cargo again the categories as values from a given byte offset. In the event you enumerate via a uncooked byte buffer you can even print the byte illustration for the pointer.
If you wish to know extra about how one can Safely handle pointers in Swift, I extremely suggest watching the linked WWDC video. It is a contemporary one, the pattern code is appropriate with Swift 5. 💪
Reminiscence binding could be harmful
You should use the bindMemory
and the asssumingMemoryBound
strategies to transform a uncooked pointer to a typed pointer. The primary will truly bind the reminiscence to a given kind, however the second operate simply returns a referenced pointer assuming it is already certain to the required kind. You may learn extra about the important thing variations right here or verify the unique UnsafeRawPointer API proposal.
let stride = MemoryLayout<Int>.stride
let alignment = MemoryLayout<Int>.alignment
let rely = 1
let byteCount = stride * rely
let rawPointer = UnsafeMutableRawPointer.allocate(byteCount: byteCount, alignment: alignment)
defer {
rawPointer.deallocate()
}
let pointer = rawPointer.bindMemory(to: Int.self, capability: rely)
pointer.initialize(repeating: 0, rely: rely)
defer {
pointer.deinitialize(rely: rely)
}
pointer.pointee = 42
print(rawPointer.load(as: Int.self))
rawPointer.storeBytes(of: 69, toByteOffset: 0, as: Int.self)
print(pointer.pointee)
Binding reminiscence could be harmful, there are a few guidelines that it’s best to observe:
- By no means return the pointer from a
withUnsafeBytes
name - Solely bind to 1 kind at a time
- Watch out with off-by-one errors
This text lists the problems that may occur in the event you re-bind a reminiscence handle.
let badPointer = rawPointer.bindMemory(to: Bool.self, capability: rely)
print(badPointer.pointee)
pointer.withMemoryRebound(to: Bool.self, capability: rely) { boolPointer in
print(boolPointer.pointee)
}
withUnsafeBytes(of: &pointer.pointee) { pointer -> Void in
for byte in pointer {
print(byte)
}
}
let bufferPointer = UnsafeRawBufferPointer(begin: pointer, rely: byteCount + 1)
for byte in bufferPointer {
print(byte)
}
I additionally suggest checking this text about reminiscence administration and byte computation in Swift. It is usually doable to repeat or transfer a reminiscence to a given vacation spot utilizing the assign(from:rely:)
or moveAssign(from:rely:)
strategies. You may learn extra about these features right here.
Opaque and managed Swift pointers
If unsafe pointers weren’t simply sufficient, it’s best to know that Swift has a number of different pointer varieties.
As Vadim Bulavin describes this in his article, with the assistance of the Unmanaged
kind you may bypass Computerized Reference Counting (ARC) that’s in any other case enforced to each Swift class. The opposite case is to transform objects between opaque pointers backwards and forwards.
class MyPoint {
let x: Int
let y: Int
init(x: Int, y: Int) {
self.x = x
self.y = y
}
deinit {
print("deinit", x, y)
}
}
let unmanaged = Unmanaged.passRetained(MyPoint(x: 4, y: 20))
unmanaged.launch()
_ = Unmanaged.passUnretained(MyPoint(x: 6, y: 9))
let opaque = Unmanaged.passRetained(MyPoint(x: 1, y: 0)).toOpaque()
Unmanaged<MyPoint>.fromOpaque(opaque).launch()
Opaque pointers are used when it’s important to work with incomplete C information constructions which can’t be represented in Swift. For instance when you’ve got a struct that accommodates a pointer kind, that variable goes to be imported to Swift as an OpaquePointer
kind when interacting with C code.
ManagedBufferPointer
and the ManagedBuffer
kind permits you to implement your individual copy-on-write information construction. This fashion you may obtain the very same habits because the built-in array, set or dictionary varieties have. Russ Bishop has an amazing publish associated to this subject.
AutoreleasingUnsafeMutablePointer
is a pointer that factors to an Goal-C reference that does not personal its goal. you may learn extra about it right here by Keith Harrison
The CVaListPointer
is a straightforward wrapper round a C va_list pointer.