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(即闭包表达式产生的结果)了,所以可以将闭包表达式进行简写(简写参数、返回值),其中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
等价于
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<不需要指定类型,这个尖括号不用写
> (两者都拓展了可变行为)
网友评论