Swift05-指针

537 阅读5分钟

Swift 进阶之路 文章汇总

前言:

上篇文章总结分析了swift的数据类型值类型引用类型,在分析的过程中提到了withUnsafePointer方法,这篇文章着重分析这个pointer指针

指针的含义

指针就是内存地址指针变量是用来存放内存地址变量,不同类型的指针变量所占用的存储单元长度相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作。

Swift中指针的分类

Swift中的指针分为两类:

  • typed pointer 指定数据类型指针,即 UnsafePointer<T>,其中T表示泛型

  • raw pointer 未指定数据类型的指针(原生指针) ,即UnsafeRawPointer

Swift与OC指针对应关系:

原生指针的使⽤ 

概念:

原生指针:是指未指定数据类型的指针,特点是对于指针内存管理是需要手动管理的,在使用完需要手动释放

假设我们想在内存中存储连续 4 个整形(100,101,102,103)的数据,我们如何使⽤raw pointer来做

/**
 * 原生指针
 * RawPionter的使⽤
 * 定义一个未知类型的指针:本质是分配32字节大小的空间,指定对齐方式是8字节对齐
*/

let p = UnsafeMutableRawPointer.allocate(byteCount: 32, alignment: 8)

/**
 * 存储
 * advanced代表当前 p 前进的步⻓,对于 RawPointer 来说,我们需要移动的是 当前存储值得内存⼤⼩即,MemoryLayout.stride
 * storeBytes: 这⾥就是存储我们当前的数据,这⾥需要制定我们当前数据的类型
 */

for i in 0..<4 {
    p.advanced(by: i * 8).storeBytes(of: i + 100, as: Int.self)
}

/**
 * 读取
 * load顾明思义是加载,fromBytesOffe:是相对于我们当前 p 的⾸地址的偏移
 */

for i in 0..<4 {
    let value = p.load(fromByteOffset: i * 8, as: Int.self)
    print("index: \(i), value: \(value)")
}

/**
 * 使用完成需要dealloc,即需要手动释放
 */
p.deallocate()

运行结果:

type pointer

typed point是指定类型的指针,我们在使用typed point的使用,会明确知道该指针指向的类型。它包含了一共四个子类型,他们分别是:

  1. UnsafePointer

  2. UnsafeMutablePointer

  3. UnsafeBufferPointer

  4. UnsafeMutableBufferPointer

**

  • UnsafePointer

**UnsafePointer 的官方描述是 访问特定类型数据的指针。意即这个指针的类型是已知。已知的内存有三种状态:

  1. 未绑定类型同时未初始化值
  2. 绑定类型但是未初始化值
  3. 绑定类型同时初始化值

这里的**UnsafePointer就是指向绑定类型同时初始化值**内存的,它相当于C系列语言中的const对象,我们不能直接初始化UnsafePointer对象,也不能修改UnsafePointer指向的值,因为这是一个常量指针

使用

使用继承UnsafePointer查看

使用swift提供的with函数

获取基本数据类型的地址是通过withUnsafePointer(to:)方法获取的,查看withUnsafePointer(to:)方法参数:

  • UnsafeMutablePointer

它相当于C系列语言中的变量指针。 

在C语言中:

int *p = malloc(sizeof(int));

在Swift中定义一个指针变量

let usmp = UnsafeMutablePointer<Int>.allocate(capacity: sizeof_sfntInstance)

两者的意义相同。
我们同样可以通过 pointee 属性发访问指针的值。同时,你还可以直接修改 pointee的值,就像我们使用Swift的其他类一样

使用:

  • UnsafeBufferPointer

UnsafeBufferPointer指代一系列连续的内存。我们可以使用这个指针作为一个序列的指针,并通过指针直接访问旗下元素.

使用:

  • UnsafeMutableBufferPointer

可变指针。UnsafeMutableBufferPointer拥有对指向序列修改的能力

使用:

raw pointer

raw pointer指未知类型的指针,这个类型相当于C语言中的(void *)类型,。
同样的,raw pointer也包含了四个子类型:

  1. UnsafeRawPointer
  2. UnsafeMutableRawPointer
  3. UnsafeBufferRawPointer
  4. UnsafeMutableBufferRawPointer
  • UnsafeRawPointer & UnsafeMutableRawPointer

UnsafeRawPointer

UnsafeRawPointer类型不提供自动内存管理,也不保证其内存,同时没有做任何的内存对齐,在使用UnsafeRawPointer的时候,应该手动对指针做内存管理,以避免泄漏或未定义的行为.

UnsafeRawPointer不能直接创建,要借助UnsafeMutableRawPointer实现

UnsafeMutableRawPointer

raw pointer 没有指定内存的类型,也没有初始化的值,他只是开辟了一块内存,也不知道它指向的那个内存中存的什么。所以它不包含pointee这个属性了。虽然我们不知道raw pointer指向的内存中存什么,但是我们可以通过raw pointeradvanced(by:)函数获取之后的地址的块区。每一次调用advanced(by:)都会返回一个地址。如果将之后的地址绑定某个类型之后,我们就可以在这个地址的片区中赋值了。

使用advanced函数根据p的地址偏移16位得到新的内存地址

绑定类型

由上图可知:绑定过raw pointer 就变成了typed pointer

  • UnsafeBufferRawPointer & UnsafeMutableBufferRawPointer

UnsafeRawBufferPointerUnsafeMutableRawBufferPointer 指代的是一系列的没有被绑定类型的内存区域.UnsafeRawBufferPointerUnsafeMutableRawBufferPointer知道它指向的内存区域,但是它并不拥有这块内存的引用。复制UnsafeRawBufferPointer 类型的变量不会复制它的内存,但是初始化一个集合到另一个新的集合过程会复制集合中的引用内存。

查看UnsafeRawBufferPointer

使用UnsafeMutableRawBufferPointer:

指针实例应用

访问结构体实例对象

其他访问方式

实例对象绑定到struct内存

根据Swift源码中的HeapObject,自定义我们自己的lg_swift_Class

struct HeapObject {
     var kind: UnsafeRawPointer
    var strongref: UInt32
    var unownedRef: UInt32
}
struct lg_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
}
class LGTeacher{
    var age = 18
}

结构体绑定:

绑定到类结构:

元组指针类型转换

获取结构体的属性的指针

withPoint系列方法

public func withUnsafeMutablePointer<T, Result>(to arg: inout T, _ body: (UnsafeMutablePointer<T>) throws -> Result) rethrows -> Result

public func withUnsafePointer<T, Result>(to arg: inout T, _ body: (UnsafePointer<T>) throws -> Result) rethrows -> Result

public func withExtendedLifetime<T, Result>(_ x: T, _ body: (T) throws -> Result) rethrows -> Result

public func withExtendedLifetime<T, Result>(_ x: T, _ body: () throws -> Result) rethrows -> Result

使用前面两个函数的概率更高,如果使用withUnsafeMutablePointer(to:) 方法修改属性值,必须保证属性的类型是var ,虽然在内部我们可以使用初始化指针的方式强行改变,但是在调用方法时,编译器会提示需要加入一个 var 的变量,而不能常量.

总结

  1. 指针类型分两种:typed pointer 指定数据类型指针,即 UnsafePointer<T>,其中T表示泛型raw pointer 未指定数据类型的指针(原生指针) ,即UnsafeRawPointer,他们的几个子类都是以unsafe开头,这同时也在告知使用者,在Swift 中使用指针不安全的。

  2. 在使用原生指针(UnsafeRawPointer)时,请指定内存的大小,使用完毕时,请及时释放.