In OC, we have made a detailed study on the classification and structure of Pointers. What about Pointers in Swift and how to use them? This article will describe its type, creation, and use
Swift Pointer introduction
Swift
There are two types of Pointers inSpecify a data type pointer (typed pointer
) 和 No data type pointer is specified (raw pointer
).raw pointer
Also called a primitive pointerraw pointer
Pointer inswift
Is used in theUnsafeRawPointer
To represent thetyped pointer
inswift
Is used in theUnsafePointer<T>
It’s generic,T
Is the type to specify.- You can find
swift
All Pointers haveUnsafe
Because theswift
Pointers operate directly on memory and are not safe.
And OC pointer
Swift
A pointer toOC
The mapping between Pointers is as follows:
Swift | OC | instructions |
---|---|---|
unsafePointer<T> |
const T * |
Pointers and the memory they point toimmutable |
unsafeMutablePointer |
T * |
Pointer and the memory to which it pointsAll can change |
unsafeRawPointer |
const void * |
Pointer toUnknown type , the memory pointed to is immutable |
unsafeMutableRawPointer |
void * |
Pointer toUnknown type , the memory pointing to is variable |
Usage of Swift pointer
- In the use of
Swift
When Pointers are created, memory management needs to be managed manually, meaning that the Pointers created need to be called at the enddeallocate
Use of raw Pointers
-
The original pointer example is as follows:
let pointer = UnsafeMutableRawPointer.allocate(byteCount: 8, alignment: 8) // Create a pointer for i in 0..<4 { // Store a pointer from the beginning of the pointer and an Int for every 8 bytes shifted pointer.advanced(by: i * 8).storeBytes(of: i+1, as: Int.self)}for i in 0..<4 { // Read from the pointer starting at the address of the pointer and then every 8 bytes shifted let value = pointer.load(fromByteOffset: i * 8, as: Int.self) print("index : \(i), value : \(value)") } pointer.deallocate() // Manually destroy Copy the code
- The steps are as follows:
- call
allocate
The function creates a8 bytes
And follow8 byte alignment
A pointer to thepointer
- in
pointer
Pointer to the first address of the store, after eachShift 8 bytes
Store a numberadvanced(by:)
Function is the storage location, if not set, will exist at the beginning of the pointer address
- Read the contents of memory by memory translation
- call
deallocate
Function on a pointerManual destroyed
- call
- The steps are as follows:
How to create a type pointer
- in
OC
Direct print in& + pointer
Can obtain the pointer address, andSwift
Here are two ways to get itType a pointer
withUnsafePointer(to:
-
WithUnsafePointer (to: creates a pointer as follows:
-
WithUnsafePointer (to:) withUnsafePointer(to:)
@inlinable public func withUnsafePointer<T.Result> (to value: inout T._ body: (UnsafePointer<T>) throws -> Result) rethrows -> Result Copy the code
- The parameter passed to the method
value
withinout
Flag, the address needs to be passed - Then the parameter
body
Is a closure that has a pointer to the type of the argument and returns a value - The last is
throws
The closurebody
The return value is thrown and thenrethrows
Throws the return value towithUnsafePointer
function
- The parameter passed to the method
-
Once we get a pointer, we can use pointer.pointee to print the value to which the pointer points
-
Since the return value Result in withUnsafePointer is of a non-fixed type, we could also write:
- At this time
pointer
Not a pointer, butint
Type, andnum
The value has changed
- At this time
-
Use of throws and REthrows:
func getAge(_ num: Int.completed: (Int) - >String) -> String { let result = completed(num) return result } / / call let age = getAge(20) { "\ [$0)" } Copy the code
- When a closure has a return value and the entire method has a return value, we usually need to get the closure’s return value and proceed
retrun
Give the method itself - While the use of
throws
andrethrows
We simply throw the closure result:
func getAge(_ num: Int.completed: (Int) throws -> String) rethrows -> String { try completed(num) } Copy the code
- When a closure has a return value and the entire method has a return value, we usually need to get the closure’s return value and proceed
-
Throws and REthrows:
-
Throws: Indicates that the function may throw an exception. When calling the function, add a try to the do catch. For example
func getAge(_ num: Int) throws -> String { return "\(num)" } do { let age = try getAge(20)}catch {} Copy the code
-
Rethrows: Indicates that the function itself does not throw an exception, but if the closure as a parameter does throw an exception, it will continue throwing the exception
-
allocate
-
Type Pointers can also be created using allocate:
let num = 20 let ptr = UnsafeMutablePointer<Int>.allocate(capacity: 1) ptr.initialize(to: num) ptr.deinitialize(count: 1) ptr.deallocate() Copy the code
- The related methods for creating code do the following:
allocate(capacity:)
: Creates memory by type,count
Is to allocate the corresponding type of memorydeallocate
: Destroy memory, andallocate
Come in pairsinitialize(to:)
:Initialize the
.to
Is a variabledeinitialize(count:)
:deinitialize
isTo initialize the
.count
isTo initialize the
Number of instances of, cannot be negative,initialize
withdeinitialize
Come in pairs
- The related methods for creating code do the following:
Some usage scenarios for the Swift pointer
Pointer to a structure type
-
Create a WSPerson structure and then create two type Pointers:
struct WSPerson { var name: String = "ws" var age: Int = 18 } let ptr = UnsafeMutablePointer<WSPerson>.allocate(capacity: 2) ptr.initialize(to: WSPerson()) // Initialize a WSPerson instance at the head of PTR memory (ptr+1).initialize(to: WSPerson(name: "dj", age: 20), pan the first address of the PTR by oneThe WSPerson memory unit stores the second WSPerson instance ptr.deinitialize(count: 2) // The number of deinitializers is 2 ptr.deallocate() Copy the code
- The code mainly through the pointer memory translation to initialize two
WSPerson
The instance (ptr+1)
fromptr
Start addressdown
translationWSPerson
Memory, you could write it this wayptr.advanced(by: 1) // This is shifted by 1 because the type is already known, so the 1 here represents a 'WSPerson' memory unit, the same as (PTR +1) Copy the code
- access
WSPerson
Examples of “can be used in the following ways:-
ptr.pointee
.(ptr+1).pointee
-
ptr[0]
.ptr[1]
-
(ptr + 1).predecessor().pointee
.ptr.successor().pointee
-
Predecessor is the pointer to the last successive WSPerson size, the next successive WSPerson size is the same as memory forerunner
- The verification results are as follows:
- The code mainly through the pointer memory translation to initialize two
Object bound to a structure type
Object the structure type to which the object is bound
-
Class Wushuang and structure HeapObject, code is as follows:
struct HeapObject { var kind: UnsafeRawPointer var strongref: UInt32 var unownedred: UInt32 } class Wushuang { var age: Int = 18 } Copy the code
- inSwift Advancements – Classes & Objects & Propertiesanalytically
Swift object
Is the nature ofHeapObject
, so write a similar structure here to bind the object
- inSwift Advancements – Classes & Objects & Propertiesanalytically
-
The code for the object binding structure is as follows:
var ws = Wushuang(a)let p = Unmanaged.passUnretained(ws as AnyObject).toOpaque() // Get the instance object let heapObj = p.bindMemory(to: HeapObject.self, capacity: 1) // Bind to a specific type Copy the code
Unmanaged.passUnretained
: Gets the instance objectws
A pointer to theUnmanaged
: It’s hosted, likeOC
In the__bridge
Transfer of ownershippassUnretained
: You don’t need ownership, you just need Pointers, and you getUnsafeMutableRawPointer
Pointer to the
bindMemory
: Binds to a specific type and has one memory sizeHeapObject
Memory size, and you get thisHeapObject
Pointer to type
-
Verification using the relevant print is as follows:
In the objectisa
The structure type bound to the class
-
We know that the kind in HeapObject is essentially isa, which refers to the class, but the structure type of the above example class is not bound yet. We know the structure of Swift class in Swift advancements – Class & Object & Properties, which can be written in pseudocode as follows:
struct swift_class_t { var isa: UnsafeRawPointer var superclass: UnsafeRawPointer var cacheData: UnsafeRawPointer var data: UnsafeRawPointer var flags: UInt32 var instanceAddressOffset: UInt32 var instanceSize: UInt32 var instanceAlignMask: UInt16 var reserved: UInt16 var classSize: UInt32 var classAddressOffset: UInt32 var description: UnsafeRawPointer } Copy the code
-
Bind heapObj’s kind to swift_class_t to get metadata:
let metadata = heapObj.pointee.kind.bindMemory(to: swift_class_t.self, capacity: 1) Copy the code
- print
metadata
The results are as follows:
- print
-
This binding is possible because essentially the real swift_class structure is the same as the pseudo-code here, so access is not affected
The use of tuple type Pointers
-
The code is as follows:
var turple = (18.22) withUnsafePointer(to: &turple) { ptr in print(ptr) } Copy the code
- In the view
ptr
Type is found to be yesUnsafePointer<(Int, Int)>
Type, and Pointers access values based on memory translation, so the type of Pointers should beUnsafePointer<Int>
To meet the requirements
- In the view
-
The assumingMemoryBound(to:) function is used in this case, as shown below
var turple = (18.22) withUnsafePointer(to: &turple) { ptr in testPointer(UnsafeRawPointer(ptr).assumingMemoryBound(to: Int.self))}func testPointer(_ t: UnsafePointer<Int>) { print(t) } Copy the code
- Due to the
ptr
Is initialized pointer, so firstptr
Equivalent toUnsafeRawPointer
, then unbind the type, but cannot be used at this timebindMemory
Because essentiallyptr
The type is already bound, so use it at this pointassumingMemoryBound
Pretending to be bindingStrong-cast new pointer, pretending to unbindInt
Type, and convert the type toUnsafePointer<Int>
- Due to the
-
The print result is as follows:
Gets a pointer to a variable in a structure
- Core code, like tuple types, needs to be used
assumingMemoryBound
Make a hypothesis binding. - There are two ways to get a pointer to a structure variable:
Get the first address and pan it
andGets the structure variable address directly
, put the two methods together:struct SHeapObject { var strongref: Int = 10 var unownedref: Int = 20 } func testPointer(_ t: UnsafePointer<Int>) { print(t) } var h = SHeapObject(a)withUnsafePointer(to: &h) { ptr in // The first way to get the first address is through memory translation to get the unownedref address testPointer(UnsafeRawPointer(ptr).assumingMemoryBound(to: Int.self)) // The second way is to get the unowNedref address directly and pretend binding let unownedred = UnsafeRawPointer(ptr) + MemoryLayout<HeapObject>.offset(of: \HeapObject.strongref)! testPointer(unownedred.assumingMemoryBound(to: Int.self))}Copy the code
offset(of:)
Is the path relative to the variable\
, the print result is as follows:
- So they get the same address
Temporarily change the memory binding type
-
The withMemoryRebound(to:, capacity:) method is used to temporarily change the binding type of the Int pointer, which needs to be converted into a UInt64 pointer. The code is as follows:
var age = 20 let ptr = withUnsafePointer(to: &age) { $0 } let uInt64Ptr = ptr.withMemoryRebound(to: UInt64.self, capacity: 1) { $0 } testPointer(uInt64Ptr) func testPointer(_ t: UnsafePointer<UInt64>) { print(t) } Copy the code
conclusion
-
Swift
There are two types of Pointers inSpecify a data type pointer (typed pointer
) 和 No data type pointer is specified (raw pointer
)
-
- There are two ways to create Pointers:
- call
withUnsafePointer(to:)
Method to create - through
allocate
Method, but needs to be calleddeallocate
For destruction
-
- Binding type:
bindMemory
: Binds a pointer to a concrete type, used to unbind an unknown type pointerassumingMemoryBound
: pretends to bind a new type for some special type, such as fetchThe structure of the body
A pointer to a variablewithMemoryRebound
: Temporarily changes the binding type for use when a pointer type type needs to be converted to another type, for exampleInt
The type pointer needs to be converted toUInt64
use