Talking about memory is inseparable from the concept of pointer, pointer is both difficult and key.

I. Introduction of Pointers

Swift also has its own types of Pointers, all of which are considered Unsafe, and its four most common types are:

  • UnsafePointer<Pointee>: similar toconst Pointee *(Read-only generic pointer)
  • UnsafeMutablePointer<Pointee>: similar toPointee *(Readable and writable generic Pointers)
  • UnsafeRawPointer: similar toconst void *(Read-only primitive type pointer)
  • UnsafeMutableRawPointer: similar tovoid *(Readable and writable primitive type pointer)

Example code 1:

var age = 10
func test1(_ ptr: UnsafeMutablePointer<Int>) {
    ptr.pointee + = 10
    print("test1 \(ptr.pointee)")
}
test1(&age) // Output: test1 20

func test2(_ ptr: UnsafePointer<Int>) {
    print("test2 \(ptr.pointee)")
}
test2(&age) // Output: test2 20

func test3(_ ptr: UnsafeMutableRawPointer) {
    ptr.storeBytes(of: 30, as: Int.self)
    print("test3 \(ptr.load(as: Int.self))")
}
test3(&age) // Output: test3 30

func test4(_ ptr: UnsafeRawPointer) {
    print("test4 \(ptr.load(as: Int.self))")
}
test4(&age) // Output: test4 30
Copy the code

Generic Pointers can read and write to memory via the pointer variable property pointee.

The raw pointer reads the memory data through the load instance method, with the parameter AS passing in the created instance type.

The raw pointer writes data through the storeBytes instance method, with the argument as passing in the type of the stored data and the argument of ‘passing in the data.

2. Get Pointers

2.1. Get a pointer to a variable

Sample code:

var age = 10
func test(_ ptr: UnsafePointer<Int>) {
    print(ptr.pointee)
}
test(&age)
Copy the code

When the function is called, the &age parameter is passed, which means that the address of the pointer is passed, and the memory of age can be accessed directly from this address. Can we just define variables when we define them?

Define a pointer to age directly:

var ptr: UnsafePointer<Int> = &age
Copy the code

The compiler is reporting an error:

We can get pointer variables using the following method:

@inlinable public func withUnsafePointer<T.Result> (to value: inout T._ body: (UnsafePointer<T>) throws -> Result) rethrows -> Result
Copy the code

Use:

var ptr = withUnsafePointer(to: &age) { $0 }
print(ptr.pointee) // Output: 10
Copy the code

foundptrisIntType pointer:

The first argument to the withUnsafePointer function is the address of the variable passed in. The second argument is the closure. The closure argument is actually the first argument to the function, and the return value is a generic type (the type of the passed argument is the same as the type of the returned value).

Mimicking the withUnsafePointer code:

func withUnsafePointer<Result.T> (to: UnsafePointer<T>, body: (UnsafePointer<T- > >)Result) -> Result {
    body(to)
}
Copy the code

WithUnsafePointer and withUnsafeMutablePointer both return generic Pointers, and you can modify the return type of both functions indirectly by changing the return type of the trailing closure.

Both UnsafeRawPointer and UnsafeMutableRawPointer have their own initializers.

var ptr = withUnsafePointer(to: &age) { UnsafeRawPointer($0)}// PTR is of type UnsafeRawPointer

var ptr2 = withUnsafeMutablePointer(to: &age) { UnsafeMutableRawPointer($0)}// Ptr2 is of type UnsafeMutableRawPointer
Copy the code

Prove to get a pointer:

var age = 10
var ptr = withUnsafePointer(to: &age) { $0 }
print(ptr) // Output: 0x000000010000C340
Copy the code

0x100001D2E + 0xA612 = 0x10000C340, which is the same as the address value printed by the pointer variable PTR, that is, the address saved by age.

2.2. Get a pointer to the heapspace instance

Sample code:

class Person {
    var age: Int
    init(age: Int) {
        self.age = age
    }
}

var person = Person(age: 10)

var ptr = withUnsafePointer(to: &person) { $0 }
print(ptr.pointee.age) // Output: 10
Copy the code

Think about:ptrWhat is stored? You store variablespersonThe address value of is still heap spacePersonObject address value?

print(ptr) // Output: 0x000000010000c4c8
print(Mems.ptr(ofVal: &person)) // Output: 0x000000010000c4c8 (address value of person variable)
print(Mems.ptr(ofRef: person)) // Output: 0x00000001006302d0 (address value of heap space Person object)
Copy the code

PTR obviously stores the address value of the Person variable. The input and return values from withUnsafePointer also reflect that PTR is storing the address of the Person variable, since the return value is whatever is passed in. A PTR is essentially a Person.

Get the address of heap space:

var person = Person(age: 10)

var ptr1 = withUnsafePointer(to: &person) { UnsafeRawPointer($0)}var personObjAddress = ptr1.load(as: UInt.self)
var ptr2 = UnsafeMutableRawPointer(bitPattern: personObjAddress)
print(ptr2) // Output: Optional(0x0000000100657f60)
Copy the code

Ptr1 stores the person address, so ptr1.load fetches the person stored address personObjAddress. The pTR2 pointer holds the heapspace address of the object.

Create a pointer

Example code one (created by malloc) :

// Create pointer to heap space (16 represents requesting 16 bytes of memory, return type UnsafeMutableRawPointer optional type)
var ptr = malloc(16)

/ / data
// Fill the first 8 bytes of the pointer with data (Int number 10)
// of: stored data
// as: data type to store
ptr?.storeBytes(of: 10, as: Int.self)
// 8 bytes after pointer to fill data (Int number 20)
// toByteOffset: byte offset (offset 8 is because Int occupies 8 bytes)
ptr?.storeBytes(of: 20, toByteOffset: 8, as: Int.self)

/ / get data
// Retrieve the first 8 bytes of memory to which the pointer points
print((ptr?.load(as: Int.self))!) // Output: 10
// Select * from memory; // Select * from memory
print((ptr?.load(fromByteOffset: 8, as: Int.self))!) // Output: 20

/ / destroy
free(ptr)
Copy the code

Pointers created by malloc must be destroyed after completion.

The sample code (through UnsafeMutableRawPointer. The allocate create) :

// Create a pointer (return value UnsafeMutableRawPointer)
// byteCount: requested byte size
// Alignment: alignment (1)
var ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)

/ / data
// Stores the first 8 bytes of data
ptr.storeBytes(of: 11, as: Int.self)
// Advanced returns a pointer of type UnsafeMutableRawPointer to the PTR offset by byte
PTR stores the last 8 bytes of Int type 22
ptr.advanced(by: 8).storeBytes(of: 22, as: Int.self)

/ / get data
print(ptr.load(as: Int.self)) // Output: 11
print(ptr.advanced(by: 8).load(as: Int.self)) // Output: 22

/ / destroy
ptr.deallocate()
Copy the code

Note that advanced returns a pointer after the specified byte has been offset.

The sample code 3 (through UnsafeMutablePointer. The allocate create) :

// Create a pointer
// The number of bytes of memory used for Int is 24 bytes.
var ptr = UnsafeMutablePointer<Int>.allocate(capacity: 3)

/ / data
// The first type of initialization: pointee: operates on the first 8 bytes
//ptr.pointee = 10

Initialize: initialize the memory space with 10 bytes (the first 8 bytes)
ptr.initialize(to: 10)

// Third way of initialization: continuous storage: continuous count*Int bytes repeat storage repeating (both memory blocks store the number 10)
// count: number of repetitions
// repeating: Repeat data
//ptr.initialize(repeating: 10, count: 2)

// Next: skip 8 bytes, return pointer (skip the first 8 bytes of pointer store 20)
ptr.successor().initialize(to: 20)
// Skip the first 16 bytes consecutively
ptr.successor().successor().initialize(to: 30)

/ / get data
// Get data in one way
// The first 8 bytes
print(ptr.pointee) // Output: 10
// Move n*8 bytes down
print((ptr + 1).pointee) // Output: 20
print((ptr + 2).pointee) // Output: 30
// Get data
// Fetch the NTH byte (8 bytes each)
print(ptr[0]) // Output: 10
print(ptr[1]) // Output: 20
print(ptr[2]) // Output: 30

// Uninitialize
ptr.deinitialize(count: 3)
/ / destroy
ptr.deallocate()
Copy the code

When using the generic pointer initialize to initialize data, deinitialize must be used (count and initialize times must be the same).

Note: UnsafeMutableRawPointer. The allocate because there is no specified type, so I need to specify the type and bytes.

UnsafeMutablePointer. The allocate is a generic pointer, so only need to tell the system how to create capacity of memory.

Example code 4:

class Person {
    var age: Int
    var name: String
    init(age: Int.name: String) {
        self.age = age
        self.name = name
    }
    deinit {
        print(name, "deinit")}}var ptr = UnsafeMutablePointer<Person>.allocate(capacity: 3)
ptr.initialize(to: Person(age: 10, name: "Jack"))
(ptr + 1).initialize(to: Person(age: 11, name: "Rose"))
(ptr + 2).initialize(to: Person(age: 12, name: "Kate"))
print("1")
ptr.deinitialize(count: 3)
print("2")
ptr.deallocate()
print("3")
Output: 1 Jack deinit Rose deinit Kate deinit 2 3 */
Copy the code

If the above code does not write deinitialize, there is no deinit output; If deinitialize(count: 2), the third initialize will not be released; Make sure you use DeInitialize correctly or you will leak memory.

Suggestion: When creating Pointers inside the function, put the code that releases the Pointers inside the defer function.

Conversion between Pointers

Sample code:

// Create a pointer
var ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)

// Convert the original pointer to a generic pointer
// to: convert the target type
ptr.assumingMemoryBound(to: Int.self).pointee = 11
(ptr + 8).assumingMemoryBound(to: Double.self).pointee = 22.0

// unsafeBitCast cast
// First argument: pointer to be converted
// Second argument: convert the target pointer type
print(unsafeBitCast(ptr, to: UnsafePointer<Int>.self).pointee) // Output: 11
print(unsafeBitCast(ptr + 8, to: UnsafePointer<Double>.self).pointee) // Output: 22.0

ptr.deallocate()
Copy the code

UnsafeBitCast is a cast that ignores the data type and does not change the original memory data due to the change of the data type.

UnsafeBitCast can also create a pointer to the heap space directly:

class Person {}var person = Person(a)print(Mems.ptr(ofRef: person)) // Output: 0x0000000100556d10
var ptr = unsafeBitCast(person, to: UnsafeRawPointer.self)
print(ptr) // Output: 0x0000000100556d10
Copy the code

Person stores the heap space address of the Person object. UnsafeBitCast takes the memory address saved by Person and converts it into a pointer of type UnsafeRawPointer to PTR. Therefore, the address saved by PTR is the same as the address saved by Person.

Another way is to take the person address and use it to create a pointer to the heap space:

var address = unsafeBitCast(person, to: UInt.self)
var ptr = UnsafeRawPointer(bitPattern: address)
Copy the code

Note: There is a difference between the primitive pointer and the generic pointer PTR + 8. The primitive pointer PTR + 8 refers to skipping 8 bytes, and the generic pointer refers to skipping 8* bytes.

For more articles in this series, please follow our wechat official account [1024 Planet].