Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2020 Swift

WWDC20 · 23 min · Swift

Unsafe Swift

What exactly makes code “unsafe”? Join the Swift team as we take a look at the programming language’s safety precautions — and when you might need to reach for unsafe operations. We’ll take a look at APIs that can cause unexpected states if not used correctly, and how you can write code more specifically to avoid undefined behavior. Learn how to work with C APIs that use pointers and the steps to take when you want to use Swift’s unsafe pointer APIs. To get the most out of this session, you should have some familiarity with Swift and the C programming language. And for more information on working with pointers, check out "Safely Manage Pointers in Swift".

Watch at developer.apple.com ↗

Transcript all transcripts

Code shown on screen · 21 snippets

Optional's force unwrapping operator swift · at 0:52 ↗
let value: Int? = nil

print(value!) // Fatal error: Unexpectedly found nil while unwrapping an Optional value
Unsafe force-unwrapping swift · at 1:58 ↗
let value: String? = "Hello"

print(value.unsafelyUnwrapped) // Hello
Invalid use of unsafe force-unwrapping swift · at 2:25 ↗
let value: String? = nil

print(value.unsafelyUnwrapped) // ?!
Invalid use of unsafe force-unwrapping swift · at 4:23 ↗
let value: String? = nil

print(value.unsafelyUnwrapped) // Guaranteed fatal error in debug builds
Manual memory management swift · at 7:37 ↗
let ptr = UnsafeMutablePointer<Int>.allocate(capacity: 1)
ptr.initialize(to: 42)
print(ptr.pointee) // 42
ptr.deallocate()
ptr.pointee = 23 // UNDEFINED BEHAVIOR
Passing an array of integers to a C function (1) objectivec · at 10:04 ↗
void process_integers(const int *start, size_t count);
Passing an array of integers to a C function (2) swift · at 10:08 ↗
func process_integers(_ start: UnsafePointer<CInt>!, _ count: Int)
Passing an array of integers to a C function (3) swift · at 10:17 ↗
let start = UnsafeMutablePointer<CInt>.allocate(capacity: 4)

start.initialize(to: 0)
(start + 1).initialize(to: 2)
(start + 2).initialize(to: 4)
(start + 3).initialize(to: 6)

process_integers(start, 4)

start.deinitialize(count: 4)
start.deallocate()
Unsafe buffer pointer types swift · at 12:33 ↗
UnsafeBufferPointer<Element>
UnsafeMutableBufferPointer<Element>

UnsafeRawBufferPointer
UnsafeMutableRawBufferPointer
Accessing contiguous collection storage swift · at 13:28 ↗
Sequence.withContiguousStorageIfAvailable(_:)
MutableCollection.withContiguousMutableStorageIfAvailable(_:)

String.withCString(_:)
String.withUTF8(_:)

Array.withUnsafeBytes(_:)
Array.withUnsafeBufferPointer(_:)
Array.withUnsafeMutableBytes(_:)
Array.withUnsafeMutableBufferPointer(_:)
Temporary pointers to Swift values swift · at 13:39 ↗
withUnsafePointer(to:_:)
withUnsafeMutablePointer(to:_:)
withUnsafeBytes(of:_:)
withUnsafeMutableBytes(of:_:)
Passing an array of integers to a C function (4) swift · at 13:48 ↗
let values: [CInt] = [0, 2, 4, 6]

values.withUnsafeBufferPointer { buffer in
  print_integers(buffer.baseAddress!, buffer.count)
}
Passing an array of integers to a C function (5) swift · at 14:25 ↗
let values: [CInt] = [0, 2, 4, 6]

print_integers(values, values.count)
Advanced C interoperability swift · at 15:36 ↗
func sysctl(
  _ name: UnsafeMutablePointer<CInt>!,
  _ namelen: CUnsignedInt,
  _ oldp: UnsafeMutableRawPointer!,
  _ oldlenp: UnsafeMutablePointer<Int>!,
  _ newp: UnsafeMutableRawPointer!,
  _ newlen: Int
) -> CInt
Advanced C interoperability swift · at 16:32 ↗
import Darwin

func cachelineSize() -> Int {
    var query = [CTL_HW, HW_CACHELINE]
    var result: CInt = 0
    var resultSize = MemoryLayout<CInt>.size
    let r = sysctl(&query, CUnsignedInt(query.count), &result, &resultSize, nil, 0)
    precondition(r == 0, "Cannot query cache line size")
    precondition(resultSize == MemoryLayout<CInt>.size)
    return Int(result)
}

print(cachelineSize()) // 64
Advanced C interoperability swift · at 18:18 ↗
import Darwin

func cachelineSize() -> Int {
    var query = [CTL_HW, HW_CACHELINE]
    return query.withUnsafeMutableBufferPointer { buffer in
        var result: CInt = 0
        withUnsafeMutablePointer(to: &result) { resultptr in
            var resultSize = MemoryLayout<CInt>.size
            let r = withUnsafeMutablePointer(to: &resultSize) { sizeptr in
                sysctl(buffer.baseAddress, CUnsignedInt(buffer.count),
                       resultptr, sizeptr,
                       nil, 0)
            }
            precondition(r == 0, "Cannot query cache line size")
            precondition(resultSize == MemoryLayout<CInt>.size)
        }
        return Int(result)
    }
}

print(cachelineSize()) // 64
Advanced C interoperability swift · at 18:30 ↗
import Darwin

func cachelineSize() -> Int {
    var query = [CTL_HW, HW_CACHELINE]
    var result: CInt = 0
    var resultSize = MemoryLayout<CInt>.size
    let r = sysctl(&query, CUnsignedInt(query.count), &result, &resultSize, nil, 0)
    precondition(r == 0, "Cannot query cache line size")
    precondition(resultSize == MemoryLayout<CInt>.size)
    return Int(result)
}

print(cachelineSize()) // 64
Closure-based vs. implicit pointers swift · at 18:48 ↗
var value = 42
withUnsafeMutablePointer(to: &value) { p in
  p.pointee += 1
}
print(value)  // 43
Closure-based vs. implicit pointers swift · at 19:19 ↗
var value = 42
withUnsafeMutablePointer(to: &value) { p in
  p.pointee += 1
}
print(value)  // 43

var value2 = 42
let p = UnsafeMutablePointer(&value2) // BROKEN -- dangling pointer!
p.pointee += 1
print(value2)
Initializing contiguous collection storage swift · at 19:43 ↗
Array.init(unsafeUninitializedCapacity:initializingWith:)
String.init(unsafeUninitializedCapacity:initializingUTF8With:)
Initializing a String value using a C function swift · at 20:02 ↗
import Darwin

func kernelVersion() -> String {
    var query = [CTL_KERN, KERN_VERSION]
    var length = 0
    let r = sysctl(&query, 2, nil, &length, nil, 0)
    precondition(r == 0, "Error retrieving kern.version")
    return String(unsafeUninitializedCapacity: length) { buffer in
        var length = buffer.count
        let r = sysctl(&query, 2, buffer.baseAddress, &length, nil, 0)
        precondition(r == 0, "Error retrieving kern.version")
        precondition(length > 0 && length <= buffer.count)
        precondition(buffer[length - 1] == 0)
        return length - 1
    }
}

print(kernelVersion())
// Darwin Kernel Version 19.5.0: Thu Apr 30 18:25:59 PDT 2020; root:xnu-6153.121.1~7/RELEASE_X86_64

Resources