Swift指针

372 阅读4分钟

指针

swift中的指针分为两类,typed pointer:指定数据类型指针,其表示为UnsafePointer<T>raw pointer未指定数据类型的指针(原生指针),其表示为UnsafePointer

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

SwiftObjective-C说明
unsafePointer< T >const T *指针及所指向的内容都不可变
unsafeMutablePointerT *指针及所指向的内容均可变
unsafeRawPointerconst void *指针指向未知类型
unsafeMutableRawPointervoid *指针指向未知类型

原生指针(raw pointer)

对于原生指针的操作,我们可以通过以下代码来操作raw pointer

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

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


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

// 4
p.deallocate()
  • 1,分配了 32 字节大小的空间,8字节对齐。
  • 2,每次移动 i * 8, 以整型的格式向该内存中存数据。
  • 3,每次偏移 i * 8,以整型结构读取内存值。
  • 4,手动管理内存空间,需要手动释放。

类型指针 (typed pointer)

类型指针可以指定该指针对应的数据类型,通过类型指针我们可以向内存中,以对象 的形式存储。

// 1
struct Person {
    var age = 18
    var height = 180
}
// 2
let ptr = UnsafeMutablePointer<Person>.allocate(capacity: 2)
// 3
ptr.initialize(to: Person())
// 4
ptr.advanced(by: 1).initialize(to: Person(age: 17, height: 170))

// 5
print(ptr[0])
// 6
print(ptr[1])

// 5
print(ptr.pointee)
// 6
print(ptr.successor().pointee)
// 7
ptr.deinitialize(count: 2)
// 8
ptr.deallocate()
  • 1,声明一个Person结构体。
  • 2,使用 类型指针 申请 2 个 Person 结构体大小的内存空间。
  • 3,4,向该内存中写入两个 Person 结构体,在这里类型已经确定,前进1个该类型空间大小的空间。
  • 5,通过 ptr[0]或 ptr.pointee的形式,在内存中读取第一个对象。
  • 6,通过 ptr[1]的形式,读取第二个对象,ptr.successor()获取下一个对象。
  • 7,8,对申请的空间进行回收。

将原生指针转化为类型指针

我们声明一个函数

func testPointer(_ p: UnsafeRawPointer) {
	// 1
    let t = p.assumingMemoryBound(to: Int.self) 
}
  • 1,假定p指针已经绑定Int类型的数据。 我们可以看到此时 变量t已经变成了 UnsafePointer<Int>类型。

绑定自定义结构

对象swift中的实例对象类对象的结构,在Swift对象一文中进行了探讨,接下来,我们将自定义swift对象结构,并和内存指针进行绑定。

自定义HeapObject

我们通过操控指针的形式,将一段内存空间绑定到我们自定义的结构体中。我们模仿系统的HeapObject结构自定义如下结构体:

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

class Person {
    var age = 18
}

我们将 Person类的结构绑定到 我们自定义的CusHeapObject

var p = Person()
// 1
let ptr = Unmanaged.passUnretained(p as AnyObject).toOpaque()
// 2
let heapObject =  ptr.bindMemory(to: CusHeapObject.self, capacity: 1)
// 3
print(heapObject.pointee)

我们看下输出结果

CusHeapObject(kind: 0x0000000100008168, strongRef: 3, unownedRef: 0)
  • 1,获取 变量p 的指针,为 UnsafeMutableRawPointer类型。
  • 2,将ptr指针指向的空间和结构体CusHeapObject进行绑定。
  • 3,输入指针引用的实例对象。

自定义swift类对象

我们自定义一个swift类对象

struct cus_swift_class {
    var kind: UnsafeRawPointer
    var superClass: UnsafeRawPointer
    // 1
    var cacheData1: UnsafeRawPointer 
    var chcheData2: UnsafeRawPointer
    var data: UnsafeRawPointer
    var flags: UInt32
    var instanceAddressOffset: UInt32
    var instanceSize: UInt32
    var flinstanceAlignMask: UInt16
    var reserved: UInt16
    var classSize: UInt32
    var description: UnsafeRawPointer
}
  • 1,swift_class中的cacheData元组结构,共 16 字节,在这里我们使用两个指针来表示。 同样我们对对象的指针进行绑定
var p = Person()
// 1
let ptr = Unmanaged.passUnretained(p as AnyObject).toOpaque()
// 2
let heapObject =  ptr.bindMemory(to: CusHeapObject.self, capacity: 1)
// 3
let metaPtr = heapObject.pointee.kind.bindMemory(to: cus_swift_class.self, capacity: 1)
print(metaPtr.pointee)

输出结果:
// cus_swift_class(kind: 0x0000000100008140, superClass: 0x00007fff8896b8f8, cacheData1: 0x00007fff201e9af0, chcheData2: 0x0000802000000000, 
// data: 0x0000000100758822, flags: 2, instanceAddressOffset: 0, instanceSize: 24, flinstanceAlignMask: 7, reserved: 0, classSize: 136, description: 0x0000000100003c40)
  • 1,获取变量 p 的指针地址ptr。
  • 2,将ptr指针绑定到我们自定义的实例对象结构上。
  • 3,将heapObject:CusHeapObjectkind指向cus_swift_class

这样我们就把p对象绑定到了我们自定义的类对象上面,为什么能够将其kind指针进行重绑定呢?因为指针所指向的内存空间和我们自定义的结构是一致的,我们才能成功的将指针和自定义结构体进行绑定。

总结

我们对swift中的原生指针类型指针做了一个简单的了解,并通过操作指针来对swift 对象进行自定义类型绑定。