Advanced Talk about Swift 5: Pointers

Today is a brief introduction to Pointers in SWIFT

Pointers in SWIFT fall into two categories

  • typed pointer Specifying a data typePointer, i.e.,UnsafePointer<T>, where T stands for generic
  • raw pointer No data type is specified(native pointer), i.eUnsafeRawPointer

The swift and OC Pointers are compared as follows:

swift OC instructions
unsafePointer< T > const T * Pointers and their contents are immutable
unsafeMutablePointer T * Pointers and the contents of memory to which they point are variable
unsafeRawPointer const void * The pointer points to an unknown type
unsafeMutableRawPointer void * The pointer points to an unknown type

The original pointer

Native pointer: a pointer with an unspecified data type

  • forPointer to thetheMemory managementIs the need toManual managementthe
  • The pointer needs to be used upHand release

What happens when you run the following code using native Pointers?

// Memory management for Pointers is manually managed // define a pointer of unknown type: Nature is the space distribution of size 32 bytes, specify the alignment is 8 bytes aligned let p = UnsafeMutableRawPointer. The allocate (byteCount: 32, alignment: 8) / / storage for I in 0.. <4 {p.torebytes (of: I + 1, as: int. self)} for I in 0.. Let value = p.log (fromByteOffset: I * 8, as: Int. Self) print("index: \(I), value: \(value)")} // Use completion requires dealloc, that is, need to manually release p.deallocate()Copy the code
  • Through the run, it was found that there was a problem reading the data because the size of each read was specified at the read, but stored in the direct 8 bytespStored in thei+1That is, the memory size for storage is not specified

  • Modification: Approvedadvanced(by:)Specifies the step size for storage
// Store for I in 0.. <4 {// Specify the number of steps to move, i.e. I * 8 p.avanced (by: I * 8). StoreBytes (of: I + 1, as: int.self)}Copy the code

After the modification, the command output is as follows

type pointer

In the previous article, we got the address of the basic data type using the withUnsafePointer(to:) method

  • To viewwithUnsafePointer(to:)The second argument is passed in the closure expression and then passedrethrowsrethrowResult(the result of the closure expression), so you can abbreviate the closure expression (the argument, the return value), where$0Represents the first argument,The $1Represents the second parameter, and so on
<! -- Definition --> @inlinable public func withUnsafePointer<T, Result>(to value: inout T, _ body: (UnsafePointer<T>) throws -> Result) rethrows -> Result <! Let p = withUnsafePointer(to: &age) {$0} print(p) <! - using 2 - > withUnsafePointer (to: & age) {print ($0)} <! <Int> let p1 = withUnsafePointer(to: &age) {PTR in return PTR}Copy the code

Due to thewithUnsafePointerA closure in a method belongs toSingle expressionTherefore, you can ignore the parameter, return value, and directly use$0, $0 is equivalent to PTR.

To access attributes

The variable value can be accessed through the pointee property of the pointer, as shown below

var age = 10 let p = withUnsafePointer(to: &age) { $0 } print(p.pointee) <! -- Print the result --> 10Copy the code

How to change the value of the age variable? There are two ways to change the value of a variable, one is indirect modification, the other is direct modification

  • Indirect changes: needs to pass directly in the closureptr.pointeeModify and return. Similar to the char*p = *p in "PD", because PD is accessed through *p
Var age = 10 age = withUnsafePointer(to: &age) {PTR in // Return ptr.pointee + 12} print(age)Copy the code
  • Modify directly - Method 1: Can also passwithUnsafemutablePointerMethods, i.e.,Creation Method 1
var age = 10
withUnsafeMutablePointer(to: &age) { ptr in
    ptr.pointee += 12
}
Copy the code
  • Direct modification mode 2: YesallocatecreateUnsafeMutablePointer, it is important to note that
    • initializedeinitializeAre in pairs
    • deinitializeThe count in the applicationcapacityNeed to agree
    • Need to bedeallicate
Let PTR = UnsafeMutablePointer<Int>. Allocate (capacity: 1) let PTR = UnsafeMutablePointer<Int>. Allocate (capacity: 1) Age) ptr.deinitialize(count: 1) ptr.pointee += 12 print(ptr.pointee)Copy the code

Pointer instance application

Practice 1: Access a structure instance object

Define a structure

Struct CJLTeacher {var age = 10 var height = 1.85} var t = CJLTeacher()Copy the code
  • There are three ways to access the CJLTeacher instance object by creating a pointer using UnsafeMutablePointer:
    • Method 1: subscript access
    • Method two: memory translation
    • Succeeded
Let PTR = UnsafeMutablePointer<CJLTeacher>.allocate(capacity: 2) // Initialize the first space ptr.initialize(to: Succeeded () // Move, initialize the second space ptr.succeeded (). Initialize (to: CJLTeacher(age: 20, height: Print (PTR [0]) print(PTR [1]) print(PTR [0]) print(PTR [1]) Succeeded () print(ptr.succeeded ().pointee) // Must be consistent with the allocation ptr.deinitialize(count: 2) // Release ptr.deallocate()Copy the code

Note that the initialization of the second space does not pass advanced(by: MemoryLayout<CJLTeacher>.stride)To access, otherwise extracting the results is a problem

  • Can be achieved byptr + 1 orsuccessor()oradvanced(by: 1)
<! PTR + 1. Initialize (to: CJLTeacher(age: 20, height: 1.75)) <! Succeeded (age: 20, height: 1.75)) <! > ptr.succeeded (). Precursor (age: 20, height: 1.75)) PTR. Advanced (by: 1). Initialize (to: CJLTeacher(age: 20, height: 1.75))Copy the code

contrast

  • Here p is usedadvanced(by: i * 8), because the specific type of P is not known at this time, and the step size of each move must be formulated
Let p = UnsafeMutableRawPointer. The allocate (byteCount: 32, alignment: 8) / / storage for I 0. In the. <4 {// Specify the number of steps to move, i.e. I * 8 p.avanced (by: I * 8). StoreBytes (of: I + 1, as: int.self)}Copy the code
  • Here is the PTR if usedadvanced(by: MemoryLayout<CJLTeacher>.stride)That is, 16*16 bytes, and the result you get is problematic, because you know the type, so you just need to identify itTake a few steps forwardAdvanced (by:1)
Let PTR = UnsafeMutablePointer<CJLTeacher>.allocate(capacity: 2) PTR. Advanced (by: 1). Initialize (to: CJLTeacher(age: 20, height: 1.75))Copy the code

Actual Practice 2: Instance objects are bound to struct memory

Define the following code

struct HeapObject {
    var kind: Int
    var strongRef: UInt32
    var unownedRef: UInt32
}

class CJLTeacher{
    var age = 18
}

var t = CJLTeacher()
Copy the code

Demo1: How does an instance object of a class bind to the structure’s memory

  • Get the memory address of the instance variable
  • 2, bind to the structure memory, return the value isUnsafeMutablePointer<T>
  • 3. Access member variablespointee.kind
/* Unmanaged memory managed by OC and CF - passUnretained reference count (s); // Retained memory managed by OC and CF. Retained reference count is not required across premises. */ let PTR = unmanaged.passunretained (t as AnyObject).toopaque () UnsafeMutablePointer<T> /* -bindMemory changes the pointer type of the current UnsafeMutableRawPointer to a specific type value - if there is no binding, */ let HeapObject = ptr.bindmemory (to: heapObject. self, capacity: 1) / / 3, access member variables print (heapObject. Pointee. Kind) print (heapObject. Pointee. StrongRef) print (heapObject. Pointee. UnownedRef)Copy the code

The result is the following, somewhat similar to the conversion of ownership when CF interacts with OC

  • create\copyYou need to useretain
  • No ownership use is requiredunretain
  • willChange the type of kind to UnsafeRawPointerThe output of kind is the address

Demo2: Bound to the class structureDefine the class structure in SWIFT as a structure

struct cjl_swift_class {
    var kind: UnsafeRawPointer
    var superClass: UnsafeRawPointer
    var cachedata1: UnsafeRawPointer
    var cachedata2: UnsafeRawPointer
    var data: UnsafeRawPointer
    var flags: UInt32
    var instanceAddressOffset: UInt32
    var instanceSize: UInt32
    var flinstanceAlignMask: UInt16
    var reserved: UInt16
    var classSize: UInt32
    var classAddressOffset: UInt32
    var description: UnsafeRawPointer
}
Copy the code
  • Change t to bind tocjl_swift_class
/ / 1, to bind to cjl_swift_class let metaPtr = heapObject. Pointee. Kind. BindMemory (to: cjl_swift_class. Self, capacity: 1) //2, access print(metaptr.pointee)Copy the code

The result of this operation is as follows, which is essentially becausemetaPtrcjl_swift_classThe class structure is the same

Practice 3: Tuple pointer type conversion

  • If you pass a tuple to a functiontestPointer, the usage mode is as follows
var tul = (10, 20) //UnsafePointer<T> func testPointer(_ p : UnsafePointer<Int>){ print(p) } withUnsafePointer(to: &tul) { (tulPtr: UnsafePointer<(Int, Int)>) in // Assumption memorybound = = = = = = = = = = = = = = = TestPointer (UnsafeRawPointer(tulPtr).AssumingMemoryBound (to: int.self))}Copy the code
  • Or tell the compiler to convert to a specific type
func testPointer(_ p: UnsafeRawPointer){
    p.assumingMemoryBound(to: Int.self)
}
Copy the code

How to get a pointer to a structure property

  • 1. Define instance variables
  • Get the address of the instance variable and pass the strongRef attribute value to the function
struct HeapObject { var strongRef: UInt32 = 10 var unownedRef: UInt32 = 20 } func testPointer(_ p: UnsafePointer(to: &t) {print(p)} UnsafePointer(to: &t) {print(p)} Let strongRef = UnsafeRawPointer(PTR) + MemoryLayout<HeapObject>.offset(of: \HeapObject.strongRef)! / / pass strongRef attribute's value testPointer (strongRef. AssumingMemoryBound (to: Int. Self))}Copy the code

Practice 5: temporarily bind memory types withMemoryRebound

  • If the type of the method does not match the type of the parameters passed in, an error is reported

Solution: PasswithMemoryReboundThe memory type is temporarily bound

var age = 10
func testPointer(_ p: UnsafePointer<Int64>){
   print(p)
}
let ptr = withUnsafePointer(to: &age) {$0}
ptr.withMemoryRebound(to: Int64.self, capacity: 1) { (ptr: UnsafePointer<Int64>)  in
    testPointer(ptr)
}
Copy the code

conclusion

  • There are two types of Pointers

    • Typed pointer Specifies a data pointer. That is, UnsafePointer

      + unsafeMutablePointer

    • Raw pointer UnsafeRawPointer + unsafeMutableRawPointer Indicates a native pointer of an unspecified data type

  • WithMemoryRebound: Changes the memory binding type temporarily

  • BindMemory (to: Capacity:): Changes the type of the memory binding. If it has not been bound before, it is the first binding. If it has been bound, it is rebound to that type

  • AssumingMemoryBound, which is basically telling the compiler: My type is this, you don’t have to check me, the actual type is still the same