Unsafe Swift: Using Pointers and Interacting With C
In this tutorial, you’ll learn how to use unsafe Swift to directly access memory through a variety of pointer types. By Brody Eller.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Unsafe Swift: Using Pointers and Interacting With C
30 mins
- Getting Started
- Examining Struct Layouts
- Using Pointers in Unsafe Swift
- Using Raw Pointers
- Using Typed Pointers
- Converting Raw Pointers to Typed Pointers
- Getting the Bytes of an Instance
- Computing a Checksum
- Three Rules of the Unsafe Club
- Don’t Return the Pointer From withUnsafeBytes!
- Only Bind to One Type at a Time!
- Don’t Walk Off the End… Whoops!
- Unsafe Swift Example 1: Compression
- Unsafe Swift Example 2: Random Generator
- Where to Go From Here?
Unsafe Swift Example 2: Random Generator
Random numbers are important for many applications, from games to machine learning.
macOS provides arc4random
, that produces cryptographically-sound random numbers. Unfortunately, this call is not available on Linux. Moreover, arc4random
only provides randoms as UInt32
. However, /dev/urandom provides an unlimited source of good random numbers.
In this section, you’ll use your new knowledge to read this file and create type-safe random numbers.
Start by creating a new playground, calling it RandomNumbers, or by opening the playground in the begin project.
Make sure to select the macOS platform this time.
Once ready, replace the default contents with:
import Foundation
enum RandomSource {
static let file = fopen("/dev/urandom", "r")!
static let queue = DispatchQueue(label: "random")
static func get(count: Int) -> [Int8] {
let capacity = count + 1 // fgets adds null termination
var data = UnsafeMutablePointer<Int8>.allocate(capacity: capacity)
defer {
data.deallocate()
}
queue.sync {
fgets(data, Int32(capacity), file)
}
return Array(UnsafeMutableBufferPointer(start: data, count: count))
}
}
You declare the file
variable static
so only one will exist in the system. You’ll rely on the system closing it when the process exits.
Since multiple threads may want random numbers, you need to protect access to it with a serial GCD queue.
The get
function is where the work happens.
First, create unallocated storage that is one beyond what you need because fgets
is always 0 terminated.
Next, get the data from the file, making sure to do so while operating on the GCD queue.
Finally, copy the data to a standard array by first wrapping it in a UnsafeMutableBufferPointer
that can act as a Sequence
.
So far, this will only safely give you an array of Int8
values. Now you’re going to extend that.
Add the following to the end of your playground:
extension BinaryInteger {
static var randomized: Self {
let numbers = RandomSource.get(count: MemoryLayout<Self>.size)
return numbers.withUnsafeBufferPointer { bufferPointer in
return bufferPointer.baseAddress!.withMemoryRebound(
to: Self.self,
capacity: 1) {
return $0.pointee
}
}
}
}
Int8.randomized
UInt8.randomized
Int16.randomized
UInt16.randomized
Int16.randomized
UInt32.randomized
Int64.randomized
UInt64.randomized
This adds a static randomized
property to all subtypes of the BinaryInteger
protocol. For more on this, check out our tutorial on protocol-oriented programming.
First, you get the random numbers. With the bytes of the array that get returned, you then rebind the Int8
values as the type requested and return a copy.
And that’s it! You’re now generating random numbers in a safe way, using unsafe Swift under the hood.
Where to Go From Here?
Congratulations on finishing this tutorial! You can download the completed project files at the top or bottom of this tutorial using the Download Materials.
There are many additional resources you can explore to learn more about using unsafe Swift:
- Swift Evolution 0107: UnsafeRawPointer API gives a detailed overview of the Swift memory model and makes reading the API documents more understandable.
- Swift Evolution 0138: UnsafeRawBufferPointer API talks extensively about working with untyped memory and has links to open-source projects that benefit from using them.
- Imported C and Objective-C APIs will give you insights about how Swift interacts with C.
I hope you’ve enjoyed this tutorial. If you have questions or experiences you would like to share, feel free to share them in the forums!