Pointers in Swift
What’s is a pointer? A pointer is a variable that shops the reminiscence deal with of a referenced object. As I discussed this in my earlier article, concerning the reminiscence structure of varied objects in Swift, a reminiscence deal with is only a hexadecimal illustration of a knowledge positioned someplace within the reminiscence. You employ cases of varied unsafe pointer varieties to entry knowledge of a particular sort in reminiscence.
Why can we need to use these sort of pointers on the first place? By default you do not have to write down unsafe Swift code, and in many of the instances you’ll be able to stay with out unsafe reminiscence pointers. These pointers come useful if you need to interoperate with different “unsafe” languages, equivalent to C. There are different low stage or legacy APIs that require the usage of handbook reminiscence administration, however you should not be afraid, when you get acquainted with unsafe Swift pointer varieties you will know much more about how reminiscence works and you may see how skinny is the layer between C libraries and Swift. 😱
What sort of pointers are there? So as to perceive pointer varieties higher in Swift, let’s get again to C only for a second. Think about the next C code instance:
#embody <stdio.h>
int essential () {
int x = 20;
int* xPointer = &x;
printf("x deal with: `%p`n", &x);
printf("x worth: `%u`n", x);
printf("pointer deal with: `%p`n", &xPointer);
printf("pointer reference: `%p`n", xPointer); // equals the deal with 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 essential.c
title, then compile & run it utilizing the clang essential.c -o essential && ./essential
command. It’ll present a fairly related output.
$ clang essential.c -o essential && ./essential
x deal with: `0x16b0c7a38`
x worth: `20`
pointer deal with: `0x16b0c7a30`
pointer reference: `0x16b0c7a38`
pointer reference worth: `20`
pointer worth `20`
tib@~: clang essential.c -o essential && ./essential
x deal with: `0x16d52fa38`
x worth: `20`
pointer deal with: `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? Properly, we merely created an integer variable and a pointer variable with an integer sort. We used the deal with of our x variable (&x) to affiliate our pointer with the reminiscence deal with of x. Now each variables factors to the identical reminiscence deal with.
We will affirm this by logging the reminiscence deal with of each variables. We will additionally alter the worth of x by updating the referenced worth of the pointer (we are able to use the * character for this) or go together 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. Let’s imagine that xPointer is only a reference to x.
Now how can we obtain the identical factor in Swift? First we’ve got to discover ways to outline a pointer sort. This is a fast record of all the unsafe pointer objects out there within the Swift customary library:
You may need observed a sample right here: Unsafe|[Mutable][Raw][Buffer]Pointer[<T>]
.
Unsafe pointers are simply direct reminiscence addresses. Every little thing that’s mutable may be modified, in different phrases you’ll be able to write to that deal with. Uncooked signifies that there is no such thing as a related (generic, T) sort 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 will all make sense in a couple of minutes. Let’s get again to our unique C pattern code and port it to Swift actual fast.
var x: Int = 20
var xPointer: UnsafeMutablePointer<Int> = .init(&x)
print("x deal with:", UnsafeRawPointer(&x));
print("x worth:", x);
print("pointer deal with:", 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* sort 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 might like to vary the worth of the referenced integer object (via the pointee property) afterward.
To print the reminiscence deal with of a variable we are able to merely use an UnsafeRawPointer
sort, as a result of we do not actually care concerning the underlying “pointee” worth, however we simply want the deal with of the reference. If you happen to print a pointer sort the debug description will comprise the underlying reminiscence deal with of the referenced object. On this case the deal with 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 approach 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 comprise some kind of leftover knowledge, so the secure method 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’ve got the allotted reminiscence storage, we are able to set the suitable pointee
values, since we have allotted the pointer with a capability of six integer values, we are able to retailer as much as 6 numbers utilizing this pointer. You need to use the superior(by:) methodology (pointer arithmetic (pointer + 5).pointee = 42
) works as effectively) to maneuver to the following deal with and set the pointee
worth of it.
The very last item I would wish to let you realize is that you should utilize 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 instantly. 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
sort 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 can we work with uncooked pointers? We have already seen how straightforward is to print out an deal with of a given worth sort utilizing an UnsafeRawPointer
reference, now it is time to join the dots and allocate some unsafe uncooked reminiscence. If it is advisable to know extra about reminiscence structure in Swift, please learn my earlier article.
Initially, we’ll must know the way a lot reminiscence to allocate. We will use the MemoryLayout struct to get information a couple of worth sort. We will use the stride and the variety of gadgets to rely how a lot byte is required to retailer our knowledge sort 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 are able to use the pointer and the superior(by:) methodology to retailer byte values of a given sort (storeBytes(of:as:)
) as uncooked bytes. We will load a given sort utilizing the load(as:) methodology. It’s value to say that if the reminiscence doesn’t comprise a worth that may be represented because the given sort, incompatible worth varieties can crash your app. ☠️
Anyway, if you happen to saved a number of values utilizing a pointer you should utilize the uncooked buffer assortment to iterate via them and cargo again the categories as values from a given byte offset. If you happen to enumerate via a uncooked byte buffer you can even print the byte illustration for the pointer.
If you wish to know extra about find out how to Safely handle pointers in Swift, I extremely advocate watching the linked WWDC video. It is a recent one, the pattern code is appropriate with Swift 5. 💪
Reminiscence binding may be harmful
You need to use the bindMemory
and the asssumingMemoryBound
strategies to transform a uncooked pointer to a typed pointer. The primary will really bind the reminiscence to a given sort, however the second perform simply returns a referenced pointer assuming it is already certain to the required sort. You may learn extra about the important thing variations right here or examine 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 may be harmful, there are a few guidelines that it’s best to comply with:
- By no means return the pointer from a
withUnsafeBytes
name - Solely bind to 1 sort at a time
- Watch out with off-by-one errors
This text lists the problems that may occur if you happen to re-bind a reminiscence deal with.
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 advocate checking this text about reminiscence administration and byte computation in Swift. It’s also 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 capabilities 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
sort you’ll be able to bypass Automated Reference Counting (ARC) that’s in any other case enforced to each Swift class. The opposite case is to transform objects between opaque pointers forwards and backwards.
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 you need to work with incomplete C knowledge constructions which can’t be represented in Swift. For instance in case you have a struct that comprises a pointer sort, that variable goes to be imported to Swift as an OpaquePointer
sort when interacting with C code.
ManagedBufferPointer
and the ManagedBuffer
sort permits you to implement your individual copy-on-write knowledge construction. This manner you’ll be able to obtain the very same conduct because the built-in array, set or dictionary varieties have. Russ Bishop has an incredible submit associated to this subject.
AutoreleasingUnsafeMutablePointer
is a pointer that factors to an Goal-C reference that does not personal its goal. you’ll be able to learn extra about it right here by Keith Harrison
The CVaListPointer
is an easy wrapper round a C va_list pointer.