Pointer basics
The computer accesses addressable memory in bytes. Machine-level programs treat memory as a very large array of bytes, called virtual memory. Each byte of this memory is identified by a unique number, which we call an address. The collection of all these addresses is called the virtual address space.
The computer performs storage, transmission, or operations on a set of binary sequences as units called words. The binary number of bits of a word is called the word length. The virtual address space is determined by the word length of the MACHINE (CPU). If the word length is N bits, the virtual address space is 0 to 2^ N-1 32 bits. The corresponding virtual address space is 4 gigabytes (4GB).
The pointer type uses the machine’s full word length, i.e. :32
position4
Bytes,64
position8
Bytes. Pointers represent addresses, which are unique numeric identifiers for each byte of virtual storage,n
The maximum address identifier of the bit-machine virtual address space is reachable2^n-1
This number identifies the address that needs to be represented and must be usedn
Bit to store.
Swift Memory Layout
Use MemoryLayout to view the size and alignment of the Swift basic data type.
MemoryLayout<Int>.size // The value is an Int, and the continuous memory usage is 8 bytes
MemoryLayout<Int>.alignment // Memory alignment of type Int is 8 bytes
The number of bytes between the start address of an Int and the start address of the next Int
MemoryLayout<Int>.stride // The step of type Int is 8 bytes
MemoryLayout<Optional<Int>>.size // 9 bytes, optional type one byte more than normal type
MemoryLayout<Optional<Int>>.alignment / / 8 bytes
MemoryLayout<Optional<Int>>.stride / / 16
MemoryLayout<Float>.size / / 4 bytes
MemoryLayout<Float>.alignment / / 4 bytes
MemoryLayout<Float>.stride / / 4 bytes
MemoryLayout<Double>.size / / 8
MemoryLayout<Double>.alignment / / 8
MemoryLayout<Double>.stride / / 8
MemoryLayout<Bool>.size / / 1
MemoryLayout<Bool>.alignment / / 1
MemoryLayout<Bool>.stride / / 1
MemoryLayout<Int32>.size / / 4
MemoryLayout<Int32>.alignment / / 4
MemoryLayout<Int32>.stride / / 4
Copy the code
Use MemoryLayout to view the size and alignment of Swift enumerations, structures, and class types.
/// Enumeration type
enum EmptyEnum {/ / / an empty enumeration
}
MemoryLayout<EmptyEnum>.size / / 0
MemoryLayout<EmptyEnum>.alignment // all addresses are divisible by 1, so they can exist at any address.
MemoryLayout<EmptyEnum>.stride / / 1
enum SampleEnum {
case none
case some(Int)}MemoryLayout<SampleEnum>.size / / 9
MemoryLayout<SampleEnum>.alignment / / 8
MemoryLayout<SampleEnum>.stride / / 16
/ / / structure
struct SampleStruct {}MemoryLayout<SampleStruct>.size / / 0
MemoryLayout<SampleStruct>.alignment // all addresses are divisible by 1, so they can exist at any address.
MemoryLayout<SampleStruct>.stride / / 1
struct SampleStruct {
let b : Int
let a : Bool
}
MemoryLayout<SampleStruct>.size // 9 but b is 16
MemoryLayout<SampleStruct>.alignment / / 8
MemoryLayout<SampleStruct>.stride / / 16
class EmptyClass {}
MemoryLayout<EmptyClass>.size/ / 8
MemoryLayout<EmptyClass>.alignment/ / 8
MemoryLayout<EmptyClass>.stride/ / 8
class SampleClass {
let b : Int = 2
var a : Bool?
}
MemoryLayout<SampleClass>.size / / 8
MemoryLayout<SampleClass>.alignment/ / 8
MemoryLayout<SampleClass>.stride/ / 8
Copy the code
Explore memory layout in more detail.
Swift pointer classification
Immutable pointer types in Swift:
Pointer | BufferPointer | |
---|---|---|
Typed | UnsafePointer<T> | UnsafeBufferPointer<T> |
Raw | UnsafeRaw Pointer |
UnsafeRaw BufferPointer |
Mutable pointer types in Swift:
Pointer | BufferPointer | |
---|---|---|
Typed | UnsafeMutablePointer<T> | UnsafeMutableBufferPointer<T> |
Raw | UnsafeMutableRaw Pointer |
UnsafeMutableRaw BufferPointer |
-
Pointer and BufferPointer: Pointer points to a single value in memory, such as Int. BufferPointer refers to multiple values (sets) of the same type in memory called buffer Pointers, such as [Int].
-
Typed Pointers Typed provide the types that interpret the byte sequence to which the pointer points. The untyped pointer Raw accesses the most primitive byte sequence and does not provide the type of the interpreted byte sequence.
-
Mutable and Immutable Pointers, read only. Mutable pointer, readable and writable.
Comparison of Swift and OC Pointers
Swift | OC | instructions |
---|---|---|
UnsafePointer<T> | const T* | A pointer is immutable; what it points to is mutable |
UnsafeMutablePointer<T> | T* | Pointers and what they point to are mutable |
UnsafeRaw Pointer |
const void* | A constant pointer to an unknown type |
UnsafeMutableRaw Pointer |
void* | Pointer to an unknown type |
Use untyped (Raw) Pointers
Example:
Use an untyped pointer to access memory and write three integers (ints).
let count = 3
let stride = MemoryLayout<Int>.stride ///8 bytes, storage space per instance
let byteCount = stride * count
let alignment = MemoryLayout<Int>.alignment
Request uninitialized memory with the specified byte size and alignment
let mutableRawPointer = UnsafeMutableRawPointer.allocate(byteCount: byteCount, alignment: alignment)
defer {
mutableRawPointer.deallocate()
}
// initialize memory
mutableRawPointer.initializeMemory(as: Int.self, repeating: 0, count: byteCount)
/// the RawPointer start position stores the integer 15
mutableRawPointer.storeBytes(of: 0xFFFFFFFFF, as: Int.self)
/// There are two ways to offset RawPointer's starting position to an integer position (8 bytes) and store an integer there:
(mutableRawPointer + stride).storeBytes(of: 0xDDDDDDD, as: Int.self)
mutableRawPointer.advanced(by: stride*2).storeBytes(of: 9, as: Int.self)
/// Use 'load' to view the value of a specific offset address in memory
mutableRawPointer.load(fromByteOffset: 0, as: Int.self) / / 15
mutableRawPointer.load(fromByteOffset: stride, as: Int.self) / / 9
/// Access memory as a collection of bytes via 'UnsafeRawBufferPointer'
let buffer = UnsafeRawBufferPointer.init(start: mutableRawPointer, count: byteCount)
for (index,byte) in buffer.enumerated(){/// iterate over the value of each byte
print("index:\(index),value:\(byte)")}/ / output
//index:0,value:255 index:8,value:221 index:16,value:9
//index:1,value:255 index:9,value:221 index:17,value:0
//index:2,value:255 index:10,value:221 index:18,value:0
//index:3,value:255 index:11,value:13 index:19,value:0
//index:4,value:15 index:12,value:0 index:20,value:0
//index:5,value:0 index:13,value:0 index:21,value:0
//index:6,value:0 index:14,value:0 index:22,value:0
//index:7,value:0 index:15,value:0 index:23,value:0
Copy the code
UnsafeRawBufferPointer represents a collection of bytes in an area of memory that can be accessed as bytes.
Uses Typed Pointers
For the above example, typed Pointers are used:
let count = 3
let stride = MemoryLayout<Int>.stride ///8 bytes, storage space per instance
let byteCount = stride * count
let alignment = MemoryLayout<Int>.alignment
// apply for uninitialized memory to store a specified number of instances of the specified type.
let mutableTypedPointer = UnsafeMutablePointer<Int>.allocate(capacity: count)
// initialize memory
mutableTypedPointer.initialize(repeating: 0, count: count)
defer {
mutableTypedPointer.deinitialize(count: count)
mutableTypedPointer.deallocate()
}
/// Store the first value
mutableTypedPointer.pointee = 15
/// Store the second value
/// 'antecedent' returns a pointer to the next continuous instance
mutableTypedPointer.successor().pointee = 10
mutableTypedPointer[1] = 10
/// Store the third value
(mutableTypedPointer + 2).pointee = 20
mutableTypedPointer.advanced(by: 2).pointee = 30
/// read from memory
mutableTypedPointer.pointee / / 15
(mutableTypedPointer + 2).pointee / / 30
(mutableTypedPointer + 2).predecessor().pointee / / 10
/// Access memory as a collection of bytes via 'UnsafeRawBufferPointer'
let buffer = UnsafeRawBufferPointer.init(start: mutableTypedPointer, count: byteCount)
for (index,byte) in buffer.enumerated(){/// iterate over the value of each byte
print("index:\(index),value:\(byte)")}/ / output
//index:0,value:15 index:8,value:10 index:16,value:30
//index:1,value:0 index:9,value:0 index:17,value:0
//index:2,value:0 index:10,value:0 index:18,value:0
//index:3,value:0 index:11,value:0 index:19,value:0
//index:4,value:0 index:12,value:0 index:20,value:0
//index:5,value:0 index:13,value:0 index:21,value:0
//index:6,value:0 index:14,value:0 index:22,value:0
//index:7,value:0 index:15,value:0 index:23,value:0
Copy the code
RawPointer is converted to TypedPointer
/// Untyped Pointers apply for uninitialized memory in bytes
let mutableRawPointer = UnsafeMutableRawPointer.allocate(byteCount: byteCount, alignment: alignment)
defer {
mutableRawPointer.deallocate()
}
/// Raw converts the volume of bytes Typed
let mutableTypedPointer = mutableRawPointer.bindMemory(to: Int.self, capacity: count)
/// initialization, not required
mutableTypedPointer.initialize(repeating: 0, count: count)
defer {
/// Just de-initialize and the first 'defer' will handle the pointer's 'deallocate'
mutableTypedPointer.deinitialize(count: count)
}
/ / / storage
mutableTypedPointer.pointee = 10
mutableTypedPointer.successor().pointee = 40
(mutableTypedPointer + 1).pointee = 20
mutableTypedPointer.advanced(by: 2).pointee = 30
/// traverse, using typed 'BufferPointer' to traverse
let buffer = UnsafeBufferPointer.init(start: mutableTypedPointer, count: count)
for item in buffer.enumerated() {
print("index:\(item.0),value:\(item.1)")}/ / output
//index:0,value:10
//index:1,value:20
//index:2,value:30
Copy the code
UnsafeBufferPointer
represents a collection of elements stored consecutively in memory that can be accessed as elements. For example, if Element’s true type is Int16, the value of Int16 stored in memory will be accessed.
Calls a function that takes a pointer
When a function is called that takes a pointer as an argument, it is possible to pass a compatible pointer type through an implicit conversion, or to pass a pointer to a variable or a pointer to an array through an implicit bridge.
Constant pointer as argument
When calling a function that takes a pointer to UnsafePointer
, we can pass the following arguments:
-
UnsafeMutablePointer UnsafePointer < Type >, < Type > or AutoreleasingUnsafeMutablePointer < Type >, according to the need to be implicitly converted to UnsafePointer < Type >.
-
If Type is Int8 or UInt8, you can pass instances of String; The string is automatically converted to UTF8 and a pointer to that UTF8 buffer is passed to the function
-
The variable variable, attribute, or subscript of Type Type is passed to the function by adding the address & to the left. (Implicit bridge)
-
An array of Type ([Type]) is passed to the function with a pointer to the beginning of the array. (Implicit bridge)
The following is an example:
/// define a function that takes' UnsafePointer
'as an argument
func functionWithConstTypePointer(_ p: UnsafePointer<Int8>) {
/ /...
}
/// Pass 'UnsafeMutablePointer
' as an argument
let mutableTypePointer = UnsafeMutablePointer<Int8>.allocate(capacity: 1)
mutableTypePointer.initialize(repeating: 10, count: 1)
defer {
mutableTypePointer.deinitialize(count: 1)
mutableTypePointer.deallocate()
}
functionWithConstTypePointer(mutableTypePointer)
/// pass 'String' as an argument
let str = "abcd"
functionWithConstTypePointer(str)
/// pass input-output variables as arguments
var a : Int8 = 3
functionWithConstTypePointer(&a)
/// pass the array of '[Type]'
functionWithConstTypePointer([1.2.3.4])
Copy the code
When calling a function that takes a pointer argument UnsafeRawPointer, we can pass the same argument as UnsafePointer
, but without the Type constraint:
The following is an example:
/// define a function that takes' UnsafeRawPointer 'as an argument
func functionWithConstRawPointer(_ p: UnsafeRawPointer) {
/ /...
}
/// Pass 'UnsafeMutablePointer
' as an argument
let mutableTypePointer = UnsafeMutablePointer<Int8>.allocate(capacity: 1)
mutableTypePointer.initialize(repeating: 10, count: 1)
defer {
mutableTypePointer.deinitialize(count: 1)
mutableTypePointer.deallocate()
}
functionWithConstRawPointer(mutableTypePointer)
/// pass 'String' as an argument
let str = "abcd"
functionWithConstRawPointer(str)
/// pass input-output variables as arguments
var a = 3.0
functionWithConstRawPointer(&a)
/// Pass an array of any type
functionWithConstRawPointer([1.2.3.4] as [Int8])
functionWithConstRawPointer([1.2.3.4] as [Int16])
functionWithConstRawPointer([1.0.2.0.3.0.4.0] as [Float])
Copy the code
Mutable pointer as argument
When calling a function that takes a pointer to UnsafeMutablePointer
, we can pass the following arguments:
UnsafeMutablePointer<Type>
The value of the.- the
Type
Type of variable, attribute, or subscript, by adding the fetch address character to the left&
Is passed to the function. (Implicit bridge) - a
Type
Type mutable array ([Type]
) is passed to the function as a pointer to an array. (Implicit bridge)
The following is an example:
/// define a function that takes' UnsafeMutablePointer
'as an argument
func functionWithMutableTypePointer(_ p: UnsafePointer<Int8>) {
/ /...
}
/// Pass 'UnsafeMutablePointer
' as an argument
let mutableTypePointer = UnsafeMutablePointer<Int8>.allocate(capacity: 1)
mutableTypePointer.initialize(repeating: 10, count: 1)
defer {
mutableTypePointer.deinitialize(count: 1)
mutableTypePointer.deallocate()
}
functionWithMutableTypePointer(mutableTypePointer)
/// Pass a variable of Type 'Type'
var b : Int8 = 10
functionWithMutableTypePointer(&b)
/// Pass a variable of Type '[Type]'
var c : [Int8] = [20.10.30.40]
functionWithMutableTypePointer(&c)
Copy the code
Similarly, when you call a function that takes a pointer argument UnsafeMutableRawPointer, you can pass the same argument as UnsafeMutablePointer
, but without the Type constraint. The following is an example:
/// define a function that takes' UnsafeMutableRawPointer 'as an argument
func functionWithMutableRawPointer(_ p: UnsafeMutableRawPointer) {
/ /...
}
/// Pass 'UnsafeMutablePointer
' as an argument
let mutableTypePointer = UnsafeMutablePointer<Int8>.allocate(capacity: 1)
mutableTypePointer.initialize(repeating: 10, count: 1)
defer {
mutableTypePointer.deinitialize(count: 1)
mutableTypePointer.deallocate()
}
functionWithMutableRawPointer(mutableTypePointer)
/// Pass any type of variable
var b : Int8 = 10
functionWithMutableRawPointer(&b)
var d : Float = 12.0
functionWithMutableRawPointer(&d)
/// Pass any type of mutable array
var c : [Int8] = [20.10.30.40]
functionWithMutableRawPointer(&c)
var e : [Float] = [20.0.10.0.30.0.40.0]
functionWithMutableRawPointer(&e)
Copy the code
Important knowledge points
The pointer created through implicit bridging of an instance or of an array’s elements is only valid during The execution of the called function. Escaping the pointer to use after the execution of the function is undefined behavior. In particular, do not use implicit bridging when calling an UnsafePointer/UnsafeMutablePointer/UnsafeRawPointer/UnsafeMutableRawPointerinitializer.
var number = 5
let numberPointer = UnsafePointer<Int> (&number)
// Accessing 'numberPointer' is undefined behavior.
var number = 5
let numberPointer = UnsafeMutablePointer<Int> (&number)
// Accessing 'numberPointer' is undefined behavior.
var number = 5
let numberPointer = UnsafeRawPointer(&number)
// Accessing 'numberPointer' is undefined behavior.
var number = 5
let numberPointer = UnsafeMutableRawPointer(&number)
// Accessing 'numberPointer' is undefined behavior.
Copy the code
what is undefined behavior?
Undefined behavior in programming languages can introduce difficult to diagnose bugs and even lead to security Back in your App.
func functionWithPointer(_ p: UnsafePointer<Int>) {
let mPointer = UnsafeMutablePointer<Int>.init(mutating: p)
mPointer.pointee = 6;
}
var number = 5
let numberPointer = UnsafePointer<Int> (&number)
functionWithPointer(numberPointer)
print(numberPointer.pointee) / / 6
print(number)//6 There are bugs that are difficult to check
number = 7
print(numberPointer.pointee) / / 7
print(number)/ / 7
Copy the code
Memory access
For any type of value in Swift,Swift provides global functions to access their Pointers or bytes in memory directly.
Access the pointer
Swift can access a pointer to any value through the following functions.
withUnsafePointer(to:_:)/// Only access, not modify
withUnsafeMutablePointer(to:_:)// It can be accessed and modified
Copy the code
Example:
/ / / only access
let temp : [Int8] = [1.2.3.4]
withUnsafePointer(to: temp) { point in
print(point.pointee)
}
/// Standard mode: access & modify
var temp : [Int8] = [1.2.3.4]
withUnsafeMutablePointer(to: &temp) { mPointer in
mPointer.pointee = [6.5.4];
}
print(temp) / / / [6, 5, 4)
Copy the code
Swift
If you want to change it by pointer, it must be a variable, and the variable must be marked as when calling the above methodinout
Parameters, that is, variables added to the left&
. Constant value, cannot beinout
Parameter in the form of an access pointer.
Error Example 1:
/// Error: access & modify
var temp : [Int8] = [1.2.3.4]
withUnsafePointer(to: &temp) { point in
let mPointer = UnsafeMutablePointer"[Int8] >.init(mutating: point)
mPointer.pointee = [6.5.4];
}
Copy the code
Here’s why:
A closure that takes a pointer to value as its sole argument. If the closure has a return value, that value is also used as the return value of the withUnsafePointer(to:_:) function. The pointer argument is valid only It is undefined behavior to try to mutate through the pointer argument by converting it to UnsafeMutablePointer or any other mutable pointer type. If you need to mutate the argument through the pointer, use withUnsafeMutablePointer(to:_:) instead.
Example 2:
var tmp : [Int8] = [1.2.3.4]
1 / / / errors
let pointer = withUnsafePointer(to: &tmp, {$0})
let mPointer = UnsafeMutablePointer"[Int8] >.init(mutating: pointer)
mPointer.pointee = [7.8.9]
2 / / / errors
let mutablePointer = withUnsafeMutablePointer(to: &tmp) {$0}
mutablePointer.pointee = [6.5.4.3]
Copy the code
Here’s why:
The pointer argument to
body
is valid only during the execution ofwithUnsafePointer(to:_:)
orwithUnsafeMutablePointer(to:_:)
. Do not store or return the pointer for later use.
When accessing memory using the above methods, remember not to return or store closure parametersbody
For later use.
Access to the byte
Swift can access bytes in memory of any value through the following functions.
withUnsafeBytes(of:_:)/// Only access, not modify
withUnsafeMutableBytes(of:_:)/// it can be accessed and modified
Copy the code
Example:
/// Only access, not modify
var temp : UInt32 = UInt32.max
withUnsafeBytes(of: temp) { rawBufferPointer in
for item in rawBufferPointer.enumerated() {
print("index:\(item.0),value:\(item.1)")}}/ / output
index:0,value:255
index:1,value:255
index:2,value:255
index:3,value:255
/// Access & modify,
var temp : UInt32 = UInt32.max / / 4294967295
withUnsafeMutableBytes(of: &temp) { mutableRawBuffer in
mutableRawBuffer[1] = 0;
mutableRawBuffer[2] = 0;
mutableRawBuffer[3] = 0;
//mutableRawBuffer[5] = 0; /// crash
}
print(temp) / / / 255
Copy the code
Examples of errors:
/// Access & modify. This method is risky and should be avoided
var temp : UInt32 = UInt32.max / / 4294967295
withUnsafeBytes(of: &temp) { rawBufferPointer in
let mutableRawBuffer = UnsafeMutableRawBufferPointer.init(mutating: rawBufferPointer)
/ / / small end of the sequence
mutableRawBuffer[1] = 0;
mutableRawBuffer[2] = 0;
mutableRawBuffer[3] = 0;
for item in mutableRawBuffer.enumerated() {
print("index:\(item.0),value:\(item.1)")}}print(temp) / / / 255
Copy the code
Here’s why:
A closure that takes a raw buffer pointer to the bytes of value as its sole argument. If the closure has a return value, that value is also used as the return value of the withUnsafeBytes(of:_:) function. The buffer pointer argument is Valid only for the duration of the closure’s execution. It is undefined behavior to attempt to mutate through the pointer by conversion to UnsafeMutableRawBufferPointer or any other mutable pointer type. If you want to mutate a value by writing through a pointer, use withUnsafeMutableBytes(of:_:) instead.
The resources
Developer.apple.com/documentati…
www.raywenderlich.com/7181017-uns…
www.vadimbulavin.com/swift-point…