This is the 12th day of my participation in the August Challenge

This article focuses on Pointers in SWIFT

Pointers in SWIFT fall into two categories

  • Typed Pointer Specifies a pointer to a data type, that is, UnsafePointer

    , where T stands for generic

  • RawPointer A pointer to an unspecified data type (native pointer), that is, UnsafeRawPointer

Comparison of swift and OC Pointers is as follows:

Swift OC instructions
unsafePointer const T * Pointers and what they point to are immutable
unsafeMutablePointer T * Pointers and the contents of memory to which they point are mutable
unsafeRawPointer const void * Pointer to unknown type
unsafeMutableRawPointer void * Pointer to unknown type

The original pointer

Native pointer: refers to a pointer that has no specified data type, as described below

  • Memory management for Pointers needs to be managed manually

  • The pointer must be released manually after use

I have the following code to use the native pointer. What happens when I run it?

// Memory management for Pointers needs to be managed manually // 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.starbytes (of: I + 1, as: int.self)} // Read for I in 0.. Print ("index: \(I), value: ") print("index: \(I), value: ") \(value)")} // Allocate allocate()Copy the code
  • The runtime found that there was a problem reading the data because the size of each read was specified, but the storage was directly in 8 bytespStored in thei+1, that is, it can be understood that there is no specified memory size for storage

  • Modify: Passadvanced(by:)Specifies the step size for storage
// Store for I in 0.. <4 {// Specify the number of current moves, that is, I * 8 p.anced (by: I * 8).storeBytes(of: I + 1, as: int.self)}Copy the code

The modified result is as follows

type pointer

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

  • To viewwithUnsafePointer(to:The second argument is passed in as a closure expression and passed throughrethrowsrethrowResult(that is, the result of the closure expression), so you can abbreviate the closure expression (shorthand parameters, return values), where$0Represents the first parameter,The $1Represents the second argument, and so on
<! @inlinable public 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)} <! UnsafePointer<Int> let p1 = withUnsafePointer(to: &age) {PTR in return PTR}Copy the code

Due to thewithUnsafePointerThe closure in the method belongs toSingle expression, so you can omit the parameter, return value, directly use$0, $0 is equivalent to PTR

To access attributes

Variable values 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 do I change the age variable?

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

  • Indirect changes: needs to be passed directly in the closureptr.pointeeModify and return. Similar to theChar *p = *p in "CJL", because CJL is accessed through *p
Var age = 10 age = withUnsafePointer(to: &age) {PTR in // return PTR. Pointee + 12} print(age)Copy the code
  • Direct Modification - 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 Method 2: PassallocatecreateUnsafeMutablePointer, it should be noted that
    • Initialize and deinitialize are paired

    • The value of Count in deinitialize must be the same as the capacity configured during application

    • Need to deallocate

Let PTR = UnsafeMutablePointer<Int>. Allocate (capacity: 1) // Initialize PTR. Initialize (to: Ptr. deinitialize(count: 1) ptr.pointee += 12 print(ptr.pointee)Copy the code

Pointer instance application

Practice 1: Access the structure instance object

Define a structure

Struct CJLTeacher {var age = 10 var height = 1.85} var t = CJLTeacher()Copy the code
  • useUnsafeMutablePointerCreate a pointer and access the CJLTeacher instance object through the pointer in the following three ways:
    • Method 1: Subscript access

    • Mode 2: Memory translation

    • Succeeded

// Allocate two CJLTeacher Spaces 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: succeeded) Print (PTR [0]) print(PTR [1]) print(PTR. Pointee) print(PTR +1). Forgoer () : forgoer () : forgoer () : forgoer () : forgoer () : forgoer () : forgoer () : forgoer () : forgoer ()Copy the code

Note that initialization of the second space does not passadvanced(by: MemoryLayout<CJLTeacher>.stride)To access, otherwise fetch results are problematic

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

contrast

  • Here p usesadvanced(by: i * 8)Because the specific type of P is not known at this time, the step size of each move must be specified
Let p = UnsafeMutableRawPointer. The allocate (byteCount: 32, alignment: 8) / / storage for I 0. In the. <4 {// Specify the number of current moves, that is, I * 8 p.anced (by: I * 8).storeBytes(of: I + 1, as: int.self)}Copy the code
  • Here,ptrIf you are usingadvanced(by: MemoryLayout<CJLTeacher>.stride)That is, 16 by 16 bytes, and the result is problematic because the specific type is known, so only identification is requiredA few steps forwardCan be, that is,advanced(by: 1)
Let PTR = UnsafeMutablePointer<CJLTeacher>. Allocate (capacity: 2) Initialize (to: CJLTeacher(age: 20, height: 1.75));Copy the code

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 is an instance object of a class bound to structure storage?

  • 1. Get the memory address of the instance variable
  • 2, bind to structure stored, return value isUnsafeMutablePointer<T>
  • 3. Access member variablespointee.kind
/* Unmanaged memory management. // passUnretained no reference count. // across the same memory as the OC/CF file, you can specify the same memory as the CF file. PassRetained increases the reference count. - toOpaque Opaque pointer */ let PTR = Unmanaged. PassUnretained (t as AnyObject).toopaque () UnsafeMutablePointer<T> /* -bindMemory Changes the pointer type of the current UnsafeMutableRawPointer to a specific type value. */ 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 ownership transition when CF interacts with OC

  • Create \copy requires retain

  • Unretain is not required to obtain ownership

  • Change kind to UnsafeRawPointer and the output of kind is the address

Demo2: Binds to the class structure

Define 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: Print (metaptr.pointee)Copy the code

The results are as follows, and the essential reason is thatmetaPtrcjl_swift_classThe class structure is the same

Practice 3: Tuple pointer type conversion

  • If you pass a tuple to a functiontestPointer, the usage is as follows
var tul = (10, 20) //UnsafePointer<T> func testPointer(_ p : UnsafePointer<Int>){ print(p) } withUnsafePointer(to: &tul) { (tulPtr: Sumingmemorybound UnsafePointer<(Int, Int)>) in // Can't use bindMemory because it's already bound to the sumingMemoryBound There is no need to check the memory binding testPointer(UnsafeRawPointer(tulPtr).sumingMemoryBound (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

Field 4: How do I get Pointers to structure attributes

  • 1. Define instance variables
  • 2. Get the address of the instance variable and pass the strongRef attribute value to the function

The code is as follows:

struct HeapObject { var strongRef: UInt32 = 10 var unownedRef: UInt32 = 20 } func testPointer(_ p: UnsafePointer<Int>){print(p)} var t = HeapObject() withUnsafePointer(to: &t) {PTR: Let strongRef = UnsafeRawPointer(PTR) + MemoryLayout<HeapObject>. Offset (of: \HeapObject.strongRef)! / / pass strongRef attribute's value testPointer (strongRef. AssumingMemoryBound (to: Int. Self))}Copy the code

Field 5: bind the memory type temporarily withMemoryRebound

  • An error is reported if the method type does not match the type of the passed parameter

Solution: bind the memory type temporarily withMemoryRebound

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 the pointer of the data type. That is, UnsafePointer

      + unsafeMutablePointer

    • RawPointer POINTER to an unspecified data type (native pointer), namely, UnsafeRawPointer + unsafeMutableRawPointer

  • WithMemoryRebound: Temporarily changes the memory binding type

  • 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 before, it will be rebound to that type

  • The assumption of memorybound is that the type of memory I have is the same as the type I’m supposed to have