Swift —— 指针
1.指针
为什么说指针是不安全的呢?主要以下几点:
- 比如我们在创建一个对象的时候,是需要在堆区分配内存空间的,但是这个内存空间的生命周期是有限的,也就意味着如果我们使用指针指向这块内存空间,如果当前内存空间的生命周期到了(也就是引用计数为0了),那么当前的指针就成了未定义的行为,也就是野指针。
- 我们创建的内存空间是有边界的,比如创建一个大小为10的数组,这个时候通过指针访问到index = 11 的位置,这个时候就越界了,访问了一个未知的内存空间。
- 指针类型和内存空间的值类型不一致,也是不安全的。
2. 指针类型
swift中的指针分为2类,typed pointer (指定数据类型指针)raw pointer(未指定数据类型指针,也叫原生指针),基本上我们接触的指针类型有以下几种
- unsafePointer,相当于oc中的const T *,指针以及指向内容都不可变
- unsafeMutablePointer,相当于oc中的 T *,指针以及指向内容都可变
- unsafeRawPointer,相当于oc中的const Void *,指针指向的内存区域未定
- unsafeMutableRawPointer,相当于oc中的const Void *,指针指向的内存区域未定
- unsafeBufferPointer,连续的内存空间
- unsafeMutableBufferPointer
- unsafeRawBufferPointer
- unsafeMutableRawBufferPointer
3. 原始指针的使用
接下来使用raw pointer 储存4个整形的数据,这里使用unsafeMutablePointer.
let p = UnsafeMutableRawPointer.allocate(byteCount: 32, alignment: 8)
for i in 0..<4 {
p.storeBytes(of: i, as: Int.self)
}
for i in 0..<4 {
let value = p.load(fromByteOffset: i * 8, as: Int.self)
print("index\(i) value:\(value)")
}
但是这里运行后发现取出来的值和想要的不一样,这是因为存的时候指针没有位移相应的位置,也就是步长。
在IOS里面有测量当前大小的工具,分别是
- MemoryLayout.size
- MemoryLayout.stride
- MemoryLayout.alignment
例如下面代码
struct LGTeacher {
var age: Int = 18
}
print(MemoryLayout<LGTeacher>.size)
print(MemoryLayout<LGTeacher>.stride)
print(MemoryLayout<LGTeacher>.alignment)
运行后发现都是8
而在结构体添加一个bool属性
struct LGTeacher {
var age: Int = 18
var sex: Bool = true
}
然后重新运行,发现这里打印的值就不一样了。这里可以知道,size是struct结构体的大小,stride是占用内存的实际大小,alignment是对其的大小。
所以这里知道,我们储存的时候先要移动 i * 步长信息的位置,然后在储存值。
for i in 0..<4 {
p.advanced(by: i * MemoryLayout<Int>.stride).storeBytes(of: i , as: Int.self)
}
这样运行后就可以看到打印的是期望的值了
当然,在使用完指针之后,需要调用deallocate来释放内存空间。
p.deallocate()
4. 泛型指针的使用
泛型指针,也叫类型指针,指定当前指针已经绑定到了具体的类型。
获取withUnsafePointer的方式有两种,一种是通过已有变量获取:
如果闭包是最后一个参数的话,可以写成尾随闭包。
var age = 18
withUnsafePointer(to: &age){ ptr in
print(ptr)
}
指针的具体内容使用pointee访问,pointee代表指针指向的具体数据类型,所以下列代码是可行的。
age = withUnsafePointer(to: &age){ ptr in
ptr.pointee + 21
}
但是需要注意的是,这里的pointee是只读属性,所以下面的代码是不可行的。
age = withUnsafePointer(to: &age){ ptr in
ptr.pointee += 21
}
但是如果使用的是withUnsafeMutablePointer的话,那么pointee就可变了。
withUnsafeMutablePointer(to: &age) { ptr in
ptr.pointee += 21
}
这个时候可以看到age的值也变了。
一种是直接内存分配:
var age = 10
let tPtr = UnsafeMutablePointer<Int>.allocate(capacity: 1)
tPtr.initialize(to: age)
这里也有两种方式来初始指针内存,一种是使用:
struct LGTeacher {
var age: Int
var height: Double
}
var tPtr = UnsafeMutablePointer<LGTeacher>.allocate(capacity: 5)
tPtr[0] = LGT