Swift(十三)-Swift的指针

997 阅读4分钟

「这是我参与2022首次更文挑战的第21天,活动详情查看:2022首次更文挑战

指针的安全性

  • 创建一个对象的时候,需要在分配内存空间。但是对象的生命周期是有限的,即内存空间的生命周期是有限的,这也就意味着如果我们使用指针指向这块内存空间,如果当前内存空间的生命周期结束了(引用计数为0),那么当前的指针将会称为野指针
  • 创建的内存空间是有边界的,如果我们创建了一个大小为N的数组,当我们通过指针访问到N+1的位置时,此时我们访问了一个未知的内存空间,将会产生越界
  • 指针类型与内存的值类型有可能不一致;

指针是不安全的

指针类型

Swift中的指针分为两类:

  • typed pointer指定数据类型指针;
  • raw pointer未指定数据类型的指针(又称为原生指针);

我们在开发过程中使用到的指针类型基本上如下表:

SwiftObject-C备注
unsafePointerconst T *指针及其指向的内容都不可变指定类型数据指针;T用来指定数据类型
unsafeMutablePointerT *指针及其指向的内容均可变
unsafeRawPointerconst void *指针指向的内存区域未定内容不可变
unsafeMutableRawPointervoid *指针指向的内存区域未定内容可变
unsafeBufferPointer连续的不可变内存空间指定类型
unsafeMutableBufferPointer连续的可变内存空间指定类型
unsafeRawBufferPointer原生的连续的不可变内存空间未指定类型
unsafeMutableRawBufferPointer原生的连续的可变内存空间

原生指针的使用

我们使用Raw Pointer来存储4个整形的数据,此时我们需要使用到UnsafeMutableRawPointer,代码如下:

image.png

我们注意到在上述代码中有这样一段代码:

p.advanced(by: i * MemoryLayout<Int>.stride).storeBytes(of: i, as: Int.self)

as: Int.self是告诉系统,我们存储的数据的类型

如果缺少了advanced那么结果就会出错,这是因为我们在内存中存储数据的时候,需要根据数据类型偏移对应的步长大小;所以在存储数据的时候,需要以p为基地址,每次存储数据都需要偏移当前数据类型的步长 * 索引

那么MemoryLayout是用来做什么的呢?

MemoryLayout

我们来通过下边一个例子来讲解一下MemoryLayout的用法:

image.png

  • size:当前类型在内存中的实际大小:8 + 16 + 1 = 25
  • stride:当前类型在内存中的步长,需要考虑内存对齐:8 + 16 + 8(1补齐为8) = 32
    • 比如当我们需要在内存中连续存储两个Teacher类型的结构体时,从第一个Teacher到第二个Teacher中间指针需要移动的大小即为步长
  • alignment:内存对齐大小;

泛型指针的使用

泛型指针通常也称为类型指针,相较于原生指针来说,泛型指针已经制定了指针的类型

image.png

**0x0000000100008080**就是我们得到的age变量的内存指针;

对于age,如果我们想要修改它应该如何做呢?

image.png

  • pointee可以得到指针执行的数据的数据类型;

需要注意的是,在withUnsafePointer的尾随闭包中是无法修改ptr.pointee的,如下所示:

image.png

withUnsafePointer中返回的pointee是不可变的;

如果我们想要修改ptr.pointee,需要使用withUnsafeMutablePointerimage.png

  • withUnsafePointer返回不可变的指针,指针的内容也不可变;
  • withUnsafeMutablePointer返回可变的指针,指针的内容可变;

指针的操作

了解了指针相关的知识,我们就可以通过指针对内存进行操作,我们来看一下下边一段代码:

image.png

在上述代码中,我们创建了一块内存空间,该内存空间允许存放6Person类型的结构体;因为我们已经知道了指针的类型,所以我们通过UnsafeMutablePointer来创建内存空间;

  • ptr相当于我们创建的内存空间的首地址,通过ptr我们可以对该内存区域进行操作;
  • deinitializedeallocate成对出现,用来回收内存;
    • deinitialize可以理解为将当前内存空间全部置为0