Swift基础语法(十七)Swift的指针实现

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第14天,点击查看活动详情

Swift基础语法文章汇总

本文主要介绍Swift的指针类型,本质也是地址,但是Swift提供专门的指针类型存储地址。

主要内容:

  1. 指针类型的认识
  2. 指针类型的使用
  3. 类型转换

1、指针认识

Swift中也有专门的指针类型,这些都被定性为“Unsafe”(不安全的),Swift中并不会简单的认为地址就是一个指针,而是有专门的类型进行包装。只要获取到地址就可以对数据进行无访问限制的操作,因此是不安全的。

指针类型:

指针类型 认识
UnsafePointer< Pointee > 类似于 const Pointee *
UnsafeMutablePointer< Pointee > 类似于 Pointee *
UnsafeRawPointer 类似于 const void *
UnsafeMutableRawPointer 类似于 void *

说明:

  1. 指针都是不安全的,因此都是Unsafe
  2. 没有Mutalbe的表示仅可读指针指向的内存,带有Mutable表示可读可写
  3. < Pointee >是泛型,表示指针的类型
  4. 带Raw的都是不支持泛型的,所以都是void类型,类型不定

2、指针的使用

2.1 带泛型

代码:

带泛型指针.webp

说明:

  1. 通过指针的pointee来拿到内存数据
  2. 依然是取地址符拿到指针,只不过需要存储到Swift提供的指针类型中
  3. 注意Mutable的可以进行修改

2.2 无泛型

代码:

无泛型指针.webp

说明:

  1. 没有设置类型,需要自己设置一下类型
  2. 注意赋值为load和取值为storeBytes即可

2.3 通过指针遍历数组

代码:

var arr = NSArray(objects: 11, 22, 33, 44)
arr.enumerateObjects { (obj, idx, stop) in
    print(idx, obj)
    if idx == 2 { // 下标为2就停止遍历
        stop.pointee = true//指针赋值
    }
}

说明:

  1. 这种遍历方式中stop参数其实就是一个指针类型()
  2. 指针拿到自己的pointee就可以进行修改了。

3、获取指针变量

3.1 获取变量的指针

3.1.1 拿到带泛型的指针

var age = 11
//带泛型指针
var ptr1 = withUnsafeMutablePointer(to: &age) { $0 }
var ptr2 = withUnsafePointer(to: &age) { $0 }
ptr1.pointee = 22
print(ptr2.pointee) // 22
print(age) // 22

说明:

  • with开头的可以获取,最后一个参数是闭包表达式

withUnsafeMutablePointer方法认识:

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

说明:

  1. 第一个参数传的就是指针
  2. 第二个参数是一个闭包表达式,获取最终的Result
  3. 闭包表达式的参数其实就是传入的指针
  4. 并且可以看到闭包表达式返回的东西就是这个withUnsafePointer返回的东西

3.1.2 拿到无泛型的指针

//无泛型指针
var ptr3 = withUnsafeMutablePointer(to: &age) { UnsafeMutableRawPointer($0) }
var ptr4 = withUnsafePointer(to: &age) { UnsafeRawPointer($0) }
ptr3.storeBytes(of: 33, as: Int.self)
print(ptr4.load(as: Int.self)) // 33
print(age) // 33

说明:

  • 这里是通过无泛型指针的初始化器来设置的,传入的就是带泛型的指针,所以可以直接传入$0

3.1.3 拿到变量的指针

代码:

变量指针.webp

说明:

  • 这里很明显ptr拿到的是person变量的地址值。指针的本意嘛,不用多言。

3.1.4 拿到对象的指针

方式一:直接获取对象地址作为指针

class Person {
    var age: Int
    init (age: Int) {
        self.age = age
    }
}
var person = Person(age: 18)
var ptr = withUnsafePointer(to: &person) { $0 }
print("变量指针:",ptr)

var personPointer = UnsafeMutableRawPointer?(bitPattern: ptr)
print("对象指针:",personPointer)

说明:

  1. 调用bitPattern初始化器,里面传入的是对象的地址,此时就可以将该地址包装成指针,也就是放到一个全局区/栈
  2. 因为传入的参数不一定是正确的,所以是可失败的初始化器

方式二:通过变量获取变量内容作为指针

对象指针.webp

说明:

  1. 这里传入person变量,unsafeBitCast函数就可以拿到这个变量的内容赋值给ptr
  2. 并且设置的类型就是UnsafeRawPointer。
  3. 通过这种方式就可以很方便的得到对象的堆空间地址

3.2 创建一个无指向的指针

方式一:

//方式一:
var ptr = malloc(16)
ptr?.storeBytes(of: 10, as: Int.self)
ptr?.storeBytes(of: 20, toByteOffset: 8, as: Int.self)
free(ptr)//释放内存

说明:

  1. malloc创建一个空间,此时拿到的ptr的类型为:(注意肯定为可选项)
  2. 按照正常的添加流程进行添加。注意总共16个字节,一次性赋值只赋给了前8个字节。所以还需要再赋后8个字节,toByteOffset是偏移量

方式二:

//方式二:
var ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
ptr.storeBytes(of: 11, as: Int.self)
ptr.advanced(by: 8).storeBytes(of: 22, as: Int.self)
print(ptr.load(as: Int.self)) // 11
print(ptr.advanced(by: 8).load(as: Int.self)) // 22
ptr.deallocate()

说明:

  1. 通过allocate进行创建,填入字节数和对齐数
  2. advanced(by: 8)是将ptr指针偏移8个字节,并且返回一个指针。所以它得到的就是后8个字节的指针
  3. 最后需要通过deallcate销毁

方式三:

//方式三:
var ptr = UnsafeMutablePointer<Int>.allocate(capacity: 3)
ptr.initialize(to: 11)
ptr.successor().initialize(to: 22)
ptr.successor().successor().initialize(to: 33)

print(ptr.pointee) // 11
print((ptr + 1).pointee) // 22
print((ptr + 2).pointee) // 33

print(ptr[0]) // 11
print(ptr[1]) // 22
print(ptr[2]) // 33

ptr.deinitialize(count: 3)
ptr.deallocate()

说明:

  1. 如果带有泛型创建指针,那么可以直接设置容量,这里的容量是多少个值,而非字节数
  2. 在设置值时,可以直接用initialize(repeating: ,count:)重复设置两个值,每个都是10
  3. 也可以用initialize()只设置第一个值
  4. ptr.successor()的作用就是偏移8个字节拿到其指针
  5. (ptr + 1)是指针偏移,直接偏移8个字节
  6. 这里ptr+1、ptr[1]等价的
  7. 最后需要销毁内存

注意:无泛型指针会进行字节偏移,而不是指针偏移:如果是泛型指针,因为已经知道占用内存大小了,所以是可以进行指针偏移的,如果是非泛型指针,不能进行指针偏移,因为不知道一次性偏移多少

4、类型转换

第一种:非泛型指针的初始化:

var ptr = withUnsafeMutablePointer(to: &age) { UnsafeMutableRawPointer($0) }
var ptr2 = withUnsafePointer(to: &age) { UnsafeRawPointer($0) }

说明:

  1. 泛型指针转非泛型指针可以通过非泛型指针的初始化器来设置

第二种:assumingMemoryBound:

ssumingMemoryBound方式转换类型.webp

说明:

  1. 非泛型指针转泛型指针可以通过这个方法来转

第三种:unsafeBitCast: 转换时会忽略数据类型的强制转换,不会因为数据类型的变化而改变原来的内存数据

unsafeBitCast类型转换.webp

说明:

  1. 正常的数据类型转换,会改变存储的数据
  2. 通过unsafeBitCast的转换不会改变内存数据

猜你喜欢

转载自juejin.im/post/7106157896521482271