美文网首页
swift pointer

swift pointer

作者: f8d1cf28626a | 来源:发表于2022-07-26 08:51 被阅读0次

swift 指针

swift中的指针分为两类

  • typed pointer 指定数据类型指针,即 UnsafePointer<T>

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

swift与OC指针对比如下:

Swift OC 说明
unsafePointer<T> const T * 指针及所指向的内容都不可变
unsafeRawPointer const void * 指针指向未知类型
unsafeMutablePointer T * 指针及其所指向的内存内容均可变
unsafeMutableRawPointer void * 指针指向未知类型

原生指针 是指未指定数据类型的指针

  • swift中 对于指针的内存管理是需要手动管理的,指针在使用完需要手动释放
//原生指针
//对于指针的内存管理是需要手动管理的
//定义一个未知类型的指针:本质是分配32字节大小的空间,指定对齐方式是8字节对齐

let roc = UnsafeMutableRawPointer.allocate(byteCount: 6, alignment: 8);

// 存贮

for a in 0..<6 {
    //指定当前移动的步数,即a * 8,一定要添加advanced方便读取
    roc.advanced(by: a * 8).storeBytes(of: a * 2, as: Int.self);
}

// 读取

for a in 0..<6 {
    let value = roc.load(fromByteOffset: a * 8, as: Int.self);
    
    print("index : \(a) value : \(value)");
}

// 结果
index : 0 value : 0
index : 1 value : 2
index : 2 value : 4
index : 3 value : 6
index : 4 value : 8
index : 5 value : 10
  • @inline 的目的是什么?

@inline 属性可以用来忽略优化级别,强制编译器遵循特定的方向。内联也可以用于混淆

@frozen 和 @inlinable 是保证这个enum, struct, function的结构不变
@frozen 是对 enum, struct 使用
@inlinable 是对 function 使用
可以保证在项目中引用的某 framework 替换后仍然不需要重新编译,

  • @frozen 的目的是什么?

将此属性应用于结构或枚举声明,以限制可以对类型进行更改的种类。仅在库演化模式下编译时才允许使用此属性。library的未来版本无法通过添加,删除或重新排列枚举的案例或结构的存储实例属性来更改声明。非冻结类型允许进行这些更改,但它们会破坏冻结类型的ABI兼容性。

  • @inlinable 的目的是什么?

@inlinable 属性允许您为特定方法启用跨模块内联。这样做了之后,方法的实现将作为模块公共接口的一部分来公开,允许编译器进一步优化来自不同模块的调用

  • @usableFromInline 的目的是什么

@usableFromInline 表明虽然该方法在内联方法中使用的,但它本身并不真正应该被内联。

type pointer

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

查看withUnsafePointer(to:的定义中,第二个参数传入的是闭包表达式,然后通过rethrows重新抛出Result(即闭包表达式产生的结果)了,所以可以将闭包表达式进行简写(简写参数、返回值),其中0表示第一个参数,1表示第二个参数,以此类推

// 官方定义
// @inlinable public func withUnsafePointer<T, Result>(to value: inout T, _ body: (UnsafePointer<T>) throws -> Result) rethrows -> Result;

var roc_age = 10

// 使用1
let roc_p = withUnsafePointer(to: &roc_age) {$0}
print(roc_p.pointee);

// 使用2
withUnsafePointer(to: &roc_age){print($0.pointee)}

// 使用3
withUnsafePointer(to: &roc_age) { pointer in
   print( pointer.pointee)
}

// 结果
10
10
10

由于withUnsafePointer方法中的闭包属于单一表达式,因此可以省略参数、返回值,直接使用0,`0等价于ptr`

如何改变age变量值?

改变变量值的方式有两种,一种是间接修改,一种是直接修改

  • 间接修改:需要在闭包中直接通过ptr.pointee修改并返回
// 使用4
roc_age = withUnsafePointer(to: &roc_age) { pointer in
   pointer.pointee + 20
}
print(roc_age)

// 结果
30
  • 直接修改-方式1:也可以通过withUnsafeMutablePointer方法
// 使用5
withUnsafePointer(to: &roc_age) { pointer in
   print(pointer.pointee + 20)
}

// 结果
50
  • 直接修改方式2:通过allocate创建UnsafeMutablePointer

  • initialize 与 deinitialize是成

  • deinitialize中的count与申请时的capacity需要一致

  • 需要deallocate

// 使用6
var roc_a = UnsafeMutablePointer<Int>.allocate(capacity: 1);
roc_a.initialize(to: 80)
roc_a.deinitialize(count: 1)
roc_a.pointee += 12
print(roc_a.pointee)
roc_a.deallocate()

指针实例应用

实战1:访问结构体实例对象

定义一个结构体

struct RocheaderObject{
    var age = 18
    var name = "roc"
}
  • 使用UnsafeMutablePointer创建指针,并通过指针访问RocheaderObject实例对象

声明和创建

// 声明
let ptr = UnsafeMutablePointer<RocheaderObject>.allocate(capacity: 3);
// 第一个空间
ptr.initialize(to: RocheaderObject())
// 第二个空间
ptr.successor().initialize(to: RocheaderObject(age: 34, name: "ROC"))
// 第三个空间
ptr.successor().successor().initialize(to: RocheaderObject(age: 101, name: "COR"))

ptr.deinitialize(count: 3); // 成对

有以下三种方式:

  • 方式一:下标访问
// 访问方式一
print(ptr[0])
print(ptr[1])
print(ptr[2])

// 结果
RocheaderObject(age: 18, name: "roc")
RocheaderObject(age: 34, name: "ROC")
RocheaderObject(age: 101, name: "COR")
  • 方式二:内存平移
// 访问方式二
print((ptr + 0).pointee)
print((ptr + 1).pointee)
print((ptr + 2).pointee)

// 结果
RocheaderObject(age: 18, name: "roc")
RocheaderObject(age: 34, name: "ROC")
RocheaderObject(age: 101, name: "COR")
  • 方式三:successor
// 访问方式三
print(ptr.pointee)
print(ptr.successor().pointee)
print(ptr.successor().successor().pointee)

ptr.deallocate() // 销毁

// 结果
RocheaderObject(age: 18, name: "roc")
RocheaderObject(age: 34, name: "ROC")
RocheaderObject(age: 101, name: "COR")

UnsafeMutablePointer<T> 空间的初始化有四种

// 空间的初始化有四种
// 声明
let ptr1 = UnsafeMutablePointer<RocheaderObject>.allocate(capacity: 4);

// 第一个空间
ptr1.initialize(to: RocheaderObject())

// 第二空间
(ptr1 + 1).initialize(to: RocheaderObject())

// 第三空间
ptr1.successor().successor().initialize(to: RocheaderObject())

// 第四空间
ptr1.advanced(by: 3).initialize(to: RocheaderObject())

ptr1.deinitialize(count: 4)
ptr1.deallocate()

UnsafeMutableRawPointer原生指针 & UnsafeMutablePointer<T>类型指针

因为此时并不知道 指针 的具体类型,必须指定每次移动的步长:使用advanced(by: i * 8)

对比

UnsafeMutableRawPointer

print(MemoryLayout<RocheaderObject>.size)

let compare_1 = UnsafeMutableRawPointer.allocate(byteCount: (2 * MemoryLayout<RocheaderObject>.size), alignment:8)

for a in 0 ..< 2 {
    
    compare_1.advanced(by: a * MemoryLayout<RocheaderObject>.size).storeBytes(of: RocheaderObject(age: 120, name: "compare_1"), as: RocheaderObject.self)
}

for b in 0 ..< 2 {
    
    let result = compare_1.load(fromByteOffset: b * MemoryLayout<RocheaderObject>.size, as: RocheaderObject.self)
    print(result)
}

compare_1.deallocate()

// 结果
RocheaderObject(age: 120, name: "compare_1")
RocheaderObject(age: 120, name: "compare_1")

注意 :swift 中 String 为 16 字节 / Int 为 8 字节

print(MemoryLayout<RocheaderObject>.size)

// 结果
24

谨慎起见:在使用指针的时候先打印结构的 size,stride,alignment (牢记 deinitialize & deallocate)

UnsafeMutablePointer<T>

let count = 2
let compare_2 = UnsafeMutablePointer<RocheaderObject>.allocate(capacity: count);

for a in 0 ..< count{
    (compare_2 + a).initialize(to: RocheaderObject(age: 190, name: "swift"))
}

for b in 0 ..< count {
    
    let result = compare_2[b]
    print(result)
}

compare_2.deinitialize(count: count)
compare_2.deallocate()

// 结果
RocheaderObject(age: 190, name: "swift")
RocheaderObject(age: 190, name: "swift")

注意:也可以用defer{}统一管理销毁对象 如:compare_1,compare_2

swift 指针 读取machO中的属性名称

实例对象绑定到struct内存

声明&创建

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

class Rocteaher {
    var age = 18
}

var t = Rocteaher()

案例1:类的实例对象如何绑定到 结构体内存中?

  • 1、获取实例变量的内存地址
  • 2、绑定到结构体内存,返回值是UnsafeMutablePointer<T>
  • 3、访问成员变量 pointee.kind
  • 4、create\copy 需要使用retain
  • 5、不需要获取所有权 使用unretain
//将t绑定到结构体内存中
//1、获取实例变量的内存地址,声明成了非托管对象
/*
 通过Unmanaged指定内存管理,类似于OC与CF的交互方式(所有权的转换 __bridge)
 - passUnretained 不增加引用计数,即不需要获取所有权
 - passRetained 增加引用计数,即需要获取所有权
 - toOpaque 不透明的指针
 */

// 1.获取实例变量的内存地址
let ptr1 = Unmanaged.passUnretained(t).toOpaque()
print(ptr1)

//2、绑定到结构体内存,返回值是UnsafeMutablePointer<T>
/*
 - bindMemory 更改当前 UnsafeMutableRawPointer 的指针类型,绑定到具体的类型值
    - 如果没有绑定,则绑定
    - 如果已经绑定,则重定向到 HeapObject类型上
 */

// 2.绑定到结构体内存

let h_object = ptr1.bindMemory(to: HeapObject.self, capacity: 1)

// 3.访问成员
print(h_object.pointee)

// 结果
HeapObject(kind: 0x000000010000c230, strongRef: 3, unownedRef: 0)

案例2:绑定到类结构

将swift中的类结构定义成一个结构体

struct roc_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
}

将【案例1】的t改成绑定到roc_swift_class

// 将【案例1】的t改成绑定到roc_swift_class
let metadataPtr = h_object.pointee.kind.bindMemory(to: roc_swift_class.self, capacity: 1)

// 访问
print(metadataPtr.pointee)

// 结果
roc_swift_class(
kind: 0x000000010000c200, 
superClass: 0x00007ff8471779b8, 
cachedata1: 0x00007ff805837800, 
cachedata2: 0x0000802000000000, 
data: 0x0000000103043be2, 
flags: 2, 
instanceAddressOffset: 0, 
instanceSize: 24, 
flinstanceAlignMask: 7, 
reserved: 0, 
classSize: 136, 
classAddressOffset: 16, 
description: 0x000000010000799c)

案例3: 元组指针类型转换

方式一

var tulpeobj = (100,600)

func testPointer1(_ p:UnsafePointer<Int>){
    print((p + 0).pointee)
    print((p + 1).pointee)
}

withUnsafePointer(to: &tulpeobj) { (ptr:UnsafePointer<(Int,Int)>) in

     //不能使用bindMemory,因为已经绑定到具体的内存中了
    //使用assumingMemoryBound,假定内存绑定,目的是告诉编译器ptr已经绑定过Int类型了,不需要再检查memory绑定

    testPointer1(UnsafeRawPointer(ptr).assumingMemoryBound(to: Int.self))
}

方式二

func testPointer2(_ p:UnsafeRawPointer)->UnsafePointer<Int>{
   // 或者告诉编译器转换成具体的类型
    return p.assumingMemoryBound(to: Int.self)
}
withUnsafePointer(to: &tulpeobj) { ptr in
    let p = testPointer2(ptr)
    print((p + 0).pointee)
    print((p + 1).pointee)
}

方式三

func testPointer3(_ p:UnsafeRawPointer){
   // 或者告诉编译器转换成具体的类型
    let roc = p.assumingMemoryBound(to: Int.self)
    print(roc.pointee)
    print(roc.successor().pointee)
    print(roc.successor().successor().pointee)
}
withUnsafePointer(to: &tulpeobj) { (ptr:UnsafePointer<(Int,Int)>) in
    testPointer3(ptr)
}

案例4: 如何获取结构体的属性的指针

struct HeapObject2 {
    var strongRef: UInt32 = 688
    var unownedRef: UInt32 = 666
}

func testPointer4(_ p:UnsafeRawPointer){
    let result = p.bindMemory(to: HeapObject2.self, capacity: 1)
    print(result.pointee.strongRef)
    print(result.pointee.unownedRef)
}

// 实例化
var t2 = HeapObject2()

withUnsafePointer(to: &t2) { ptr in
    // 获取变量
    testPointer4(ptr)
}

案例5: 通过 withMemoryRebound 临时绑定内存类型

var roc_age = 17

func testPointer5(_ p:UnsafePointer<Int64>){
    print(p.pointee)
}

let ptr3 = withUnsafePointer(to: &roc_age){$0}

// 临时绑定
ptr3.withMemoryRebound(to: Int64.self, capacity: 1) { ptr in
    testPointer5(ptr)
}

总结

  • withMemoryRebound: 临时更改内存绑定类型
  • bindMemory(to: Capacity:): 更改内存绑定的类型,如果之前没有绑定,那么就是首次绑定,如果绑定过了,会被重新绑定为该类型
  • assumingMemoryBound假定内存绑定,这里就是告诉编译器:我的类型就是这个,你不要检查我了,其实际类型还是原来的类型

指针 其实只有两种

  • UnsafePointer<需要指定类型> & UnsafeRawPointer<不需要指定类型,这个尖括号不用写> (两者都拓展了可变行为)

相关文章

网友评论

      本文标题:swift pointer

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