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

  • SwiftThere are two types of Pointers inSpecify a data type pointer (typed pointer)No data type pointer is specified (raw pointer).raw pointerAlso called a primitive pointer
    • raw pointerPointer inswiftIs used in theUnsafeRawPointerTo represent the
    • typed pointerinswiftIs used in theUnsafePointer<T>It’s generic,TIs the type to specify.
    • You can findswiftAll Pointers haveUnsafeBecause theswiftPointers operate directly on memory and are not safe.

And OC pointer

  • SwiftA pointer toOCThe 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 ofSwiftWhen 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:
      • callallocateThe function creates a8 bytesAnd follow8 byte alignmentA pointer to thepointer
      • inpointerPointer to the first address of the store, after eachShift 8 bytesStore a number
        • advanced(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
      • calldeallocateFunction on a pointerManual destroyed

How to create a type pointer

  • inOCDirect print in& + pointerCan obtain the pointer address, andSwiftHere 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 methodvaluewithinoutFlag, the address needs to be passed
    • Then the parameterbodyIs a closure that has a pointer to the type of the argument and returns a value
    • The last isthrowsThe closurebodyThe return value is thrown and thenrethrowsThrows the return value towithUnsafePointerfunction
  • 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 timepointerNot a pointer, butintType, andnumThe value has changed
  • 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 proceedretrunGive the method itself
    • While the use ofthrowsandrethrowsWe simply throw the closure result:
    func getAge(_ num: Int.completed: (Int) throws -> String) rethrows -> String {
        try completed(num)
    }
    Copy the code
  • 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,countIs to allocate the corresponding type of memory
      • deallocate: Destroy memory, andallocateCome in pairs
      • initialize(to:):Initialize the.toIs a variable
      • deinitialize(count:):deinitializeisTo initialize the.countisTo initialize theNumber of instances of, cannot be negative,initializewithdeinitializeCome in pairs

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 twoWSPersonThe instance
    • (ptr+1)fromptrStart addressdowntranslationWSPersonMemory, you could write it this way
      ptr.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
    • accessWSPersonExamples of “can be used in the following ways:
        1. ptr.pointee.(ptr+1).pointee
        1. ptr[0].ptr[1]
        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:



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 & PropertiesanalyticallySwift objectIs the nature ofHeapObject, so write a similar structure here to bind the object
  • 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 objectwsA pointer to the
      • Unmanaged: It’s hosted, likeOCIn the__bridgeTransfer of ownership
      • passUnretained: You don’t need ownership, you just need Pointers, and you getUnsafeMutableRawPointerPointer to the
    • bindMemory: Binds to a specific type and has one memory sizeHeapObjectMemory size, and you get thisHeapObjectPointer to type
  • Verification using the relevant print is as follows:



In the objectisaThe 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
    • printmetadataThe results are as follows:



  • 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 viewptrType 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
  • 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 theptrIs initialized pointer, so firstptrEquivalent toUnsafeRawPointer, then unbind the type, but cannot be used at this timebindMemoryBecause essentiallyptrThe type is already bound, so use it at this pointassumingMemoryBound Pretending to be bindingStrong-cast new pointer, pretending to unbindIntType, and convert the type toUnsafePointer<Int>
  • The print result is as follows:

Gets a pointer to a variable in a structure

  • Core code, like tuple types, needs to be usedassumingMemoryBoundMake a hypothesis binding.
  • There are two ways to get a pointer to a structure variable:Get the first address and pan itandGets 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

    1. SwiftThere are two types of Pointers inSpecify a data type pointer (typed pointer)  和 No data type pointer is specified (raw pointer)
    1. There are two ways to create Pointers:
    • callwithUnsafePointer(to:)Method to create
    • throughallocateMethod, but needs to be calleddeallocateFor destruction
    1. Binding type:
    • bindMemory: Binds a pointer to a concrete type, used to unbind an unknown type pointer
    • assumingMemoryBound: pretends to bind a new type for some special type, such as fetchThe structure of the bodyA pointer to a variable
    • withMemoryRebound: Temporarily changes the binding type for use when a pointer type type needs to be converted to another type, for exampleIntThe type pointer needs to be converted toUInt64use