美文网首页Swift
Swift中的指针

Swift中的指针

作者: YY323 | 来源:发表于2021-01-05 17:16 被阅读0次

指针

Swift中指针分为两类:

  • typed pointer:指定数据类型指针,UnsafePointer<T>,T表示泛型
  • raw pointer:未指定数据类型指针(原生指针),UnsafeRawPointer

Swift 中的指针和 OC 中指针的对应关系如下:

  • Raw Pointer的使用

注意: 指针的内存管理需要手动管理,使用完后需要手动释放

例:使用Raw Pointer在内存中连续存储4个整型数据

// 以8字节对齐,分配32字节大小的内存空间
let p = UnsafeMutableRawPointer.allocate(byteCount: 32, alignment: 8)

// 存储值到内存
for i in 0..<4 {
    // 通过advanced(by:)指定存储时的步长
    p.advanced(by: i * 8).storeBytes(of: i + 1, as: Int.self)
}

// 从内存中读取值
for i in 0..<4 {
    // 通过内存平移来读取内存中的值
    let value = p.load(fromByteOffset:i * 8, as: Int.self)
    print("index\(i), value:\(value)")
}
// 指针的内存管理需要手动管理
p.deallocate()
  • Type Pointer的使用
  • withunsafePointer(to:)

首先了解Swift$0$1的含义:
Swift自动为闭包提供参数名缩写功能,可以直接通过$0$1 来表示闭包中的第一个、第二个参数,并且对应的参数类型会根据函数类型来进行判断。

在前面,我们获取一个变量本身的地址都是通过 withUnsafePointer(to:) 的方式获取。
其函数定义如下:

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

可看到:

  • 第一个参数是通过 inout 修饰的泛型,前面说过 inout 用来修饰的参数表示传递的是地址
  • 第二个参数是一个闭包表达式,通过 rethrows 重新抛给 Result(即闭包表达式产生的结果),所以可以通过简写闭包的参数返回值,简写如下:
var age : Int = 10

// 第一种:单一表达式:相当于将ptr赋值给p,p的类型UnsafePointer<Int>
let p = withUnsafePointer(to: &age) { ptr in
    return ptr
}

// 第二种:此时p为print($0)的返回结果,类型为print方法的返回类型Void
let p = withUnsafePointer(to: &age) { print($0) }

// 第三种:$0赋值给p,p的类型UnsafePointer<Int>
let p = withUnsafePointer(to: &age) { $0 }

从上可知:withUnsafePointer 这个函数result 类型取决于其参数闭包表达式的返回值,第一种和第三种得到的 p 都是UnsafePointer<Int>类型,可以通过 p.pointee 获取 age 的值。

还可以使用 withUnsafePointer 这个函数修改 age 的值:

age = withUnsafePointer(to: &age, { ptr in
    return ptr.pointee + 12
})
  • withUnsafeMutablePointer(to:)
    如果想在闭包表达式中直接修改age的值则需要用到 withUnsafeMutablePointer
withUnsafeMutablePointer(to: &age, { ptr in
    ptr.pointee = 122
})

执行 上面代码,age 的值就直接被修改为 122 了。

  • UnsafeMutablePointer
// 分配容量为1的内存空间(Int8字节)
let ptr = UnsafeMutablePointer<Int>.allocate(capacity: 1)

// 初始化
ptr.initialize(to: 22)

// 手动管理内存:对应initialize
ptr.deinitialize(count: 1)

// 手动管理内存:对应allocate
ptr.deallocate()

这时 ptr.pointee 读出来则为22。

指针实例演示

实例1:访问结构体指针

struct YYTeacher {
    var age = 12
    var name = "YY"
}

var t = YYTeacher()

// 分配装2个struct的内存空间
let ptr = UnsafeMutablePointer<YYTeacher>.allocate(capacity: 2)

// 初始化第一个struct的内存空间
ptr.initialize(to: YYTeacher())

// 第一种方式:初始化第二个struct的内存空间
ptr.successor().initialize(to: YYTeacher(age: 22, name: "EE"))

// 第二种方式:初始化第二个struct的内存空间
//(ptr + 1).initialize(to: YYTeacher(age: 22, name: "EE"))

// 第三种方式:初始化第二个struct的内存空间
//ptr.advanced(by: 1).initialize(to:YYTeacher(age: 22, name: "EE"))

// 两种方式获取第一个struct的值
print(ptr[0])
print(ptr.pointee)

// 三种方式获取第二个struct的值
print(ptr[1])
print((ptr + 1).pointee)
print(ptr.successor().pointee)
print(ptr.advanced(by: 1).pointee)

// 手动管理内存:对应initialize
ptr.deinitialize(count: 2)

// 手动管理内存:对应allocate
ptr.deallocate()

通过查看源码可知:successor() 本质就是 advanced(by:1)

对比上面 Raw Pointer 例子中的p.advanced(by: i * 8) ,因为上面的 p未知类型,要存储Int类型的值就必须每次指定移动 8 的倍数,这里的8是一个Int类型所占的字节数;而在 UnsafeMutablePointer 中初始化第二个struct内存空间时使用 ptr.advanced(by:1) ,这里已知 ptr结构体指针,只需要告知往前移动几步就可以了,这里的 1 相当于往前移动 1个struct步长 (这里的 struct 步长为24)就好,两个 struct 相差的字节大小为1 * 24
实例2:将实例对象绑定到结构体内存中

struct HeapObject {
    var kind: Int
    var strongRef: UInt32
    var unownedRef: UInt32
}

class YYTeacher {
    var age = 20
}

var t = YYTeacher()

首先:获取实例对象的值
其次:将 raw pointer 绑定到具体类型指针

// 获取实例对象的值,返回值类型为 UnsafeMutableRawPointer
let ptr = Unmanaged.passUnretained(t as AnyObject).toOpaque()

// 将当前Raw Pointer-->ptr重新绑定到HeapObject结构体,返回值类型为 UnsafeMutablePointer<HeapObject>
let heapObject = ptr.bindMemory(to: HeapObject.self, capacity: 1)
// 访问内存中的值
print(heapObject.pointee)
  • Unmanaged 取代手动管理内存,用来托管指针,可以指定内存管理,类似于 OCCF 交互时的 CoreFoundation( __brige ,有所有权的转换),在这里将实例对象 t 声明成了非托管 对象给到 ptr
  • passRetained增加引用计数,需要获取所有权,如OC中的create、copy关键字在声明过程中就需要所有权
  • passUnretained不增加引用计数,不需要获取其所有权,不再需要手动去释放如 CF 中的 CFRelese,在这里只需要获得其指针,不需要获取其所有权
  • bindMemory
    如果绑定,则绑定
    如果绑定,则重定向到指定类型内存
  • 如果这里将 var kind: Int 改成 var kind: UnsafeRawPointer ,在print(heapObject.pointee.kind) 时打印的则是地址

扩展:如何将 heapObject 的属性 kind 绑定到 Swift 类结构的结构体内存中?(此时的 kindUnsafeRawPointer 类型)

  • 首先定义个swift类结构的结构体:
struct 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 instanceAlignMask:UInt16
    var reserved:UInt16
    var classSize:UInt32
    var classAddressOffset:UInt32
    var description: UnsafeRawPointer
}
  • kind 绑定到 swift_class 这个结构体的内存中
// 绑定内存
let metaptr = heapObject.pointee.kind.bindMemory(to: swift_class.self, capacity: 1)
// 访问内存中的值
print(metaptr.pointee)

运行结果如下图:

其本质是因为 metaptrswift_class 的内存结构是一样的。

实例3:元祖指针类型转换
如何将元祖 tul 的指针传给 testPointer 函数?

var tul = (10,20)

func testPointer(_ p: UnsafePointer<(Int)>) {
    print(p)
}

在上面例子中,testPointer 函数的参数是一个UnsafePointer<(Int)> 类型的,而 tul 的指针类型为 UnsafePointer<(Int, Int)> ,这时就需要将 tul 的指针类型重新绑定内存。如下:

withUnsafePointer(to: &tul) {(tulPtr : UnsafePointer<(Int, Int)>) in
    // 这里不能使用bindMemory,因为tulPtr已经绑定到具体类型了
    // assumingMemoryBound:假定内存绑定,告诉编译器tulPtr已经绑定过Int类型了,不需要再检查memory绑定
    testPointer(UnsafeRawPointer(tulPtr).assumingMemoryBound(to: Int.self))
}

那么:bindMemory(to:capacity:)assumingMemoryBound(to:)以及withMemoryRebound(to:capacity:_:)用法有什么区别呢?

  • bindMemory(to:capacity:):如果以前未绑定,调用后则首次绑定到指定类型的内存;如果以前已绑定,调用后则重新绑定到指定类型(这里编译器会检查之前绑定的类型和现在指定的类型是否布局兼容)。

  • assumingMemoryBound(to:)假定内存绑定,重新绑定到指定类型的内存,告诉编译器不用检查类型(绕过编译器检查类型)

  • withMemoryRebound(to:capacity:_:)临时更改内存绑定类型。适用于已初始化的类型指针;使用此方法时,重新绑定的类型要与之前的指针类型具有相同 sizestride

var age = 12

func testPointer(_ p: UnsafePointer<UInt64>) {
   print(p)
}

let ptr = withUnsafePointer(to: &age){$0}
ptr.withMemoryRebound(to: UInt64.self, capacity: 1) {
   testPointer($0)
}

在这里 ptr 本身是 Int 类型的指针,使用 withMemoryRebound临时将其指针类型绑定到 UInt64 类型的内存,使用结束后,其类型重新绑定成 Int 类型的指针。

相关文章

  • Swift指针|内存管理

    一、Swift指针 1.Swift指针简介 swift中的指针分为两类 typed pointer 指定数据类型指...

  • swift pointer

    swift 指针 swift中的指针分为两类 typed pointer 指定数据类型指针,即 UnsafePoi...

  • Swift进阶 04:指针

    本文主要介绍Swift中的指针 Swift中的指针主要分为两类 typed pointer 指定数据类型的指针,即...

  • Swift-进阶 04:指针

    本文主要介绍swift中的指针 swift中的指针分为两类 typed pointer 指定数据类型指针,即 Un...

  • [转] Swift 中的指针使用

    原文Swift 中的指针使用

  • swift中的指针

    Swift 中的指针使用 本文转载自OneV's Den的blogApple 期望在 Swift 中指针能够尽量减...

  • Swift-04:指针

    今天我们来看看swift中的指针是如何如何使用的 swift中的指针分为两类 typed pointer 指定数据...

  • Swift 指针

    Swift 指针 前言 指针,作为编程中最重要的概念,一直存在于各大语言中,下面我们就来探索一下Swift中的指针...

  • 纯代码讲解swift的指针

    swift的指针: Objective-C和C语言经常需要使用到指针。Swift中的数据类型由于良好的设计,...

  • Swift指针&内存管理

    一、指针    1、指针类型   Swift中的指针分为两类:指定数据类型的指针(typed pointer);未...

网友评论

    本文标题:Swift中的指针

    本文链接:https://www.haomeiwen.com/subject/xexhoktx.html