四种指针类型
@frozen struct UnsafePointer<Pointee> 相当于const Pointee *(常量指针,顾名思义无法修改) 其中Pointee是个泛型,他可以指代类型,比如UnsafePointer<Int>就是 const Int *
@frozen struct UnsafeMutablePointer<Pointee> 相当于 Pointee * 就是普通指针,能修改的
@frozen struct UnsafeRawPointer 相当于 const void * 无符号指针,不能修改
@frozen struct UnsafeMutableRawPointer 相当于void * 能修改
从属性上看所有的指针都是结构体类型,和Unsafe,一般unsafe就比较底层,平时调试使用,正式开发最好别用
var a:Int = 10
func test(_ point:UnsafePointer<Int>) {
print(point) //0x000000010000c1f8
print(point.pointee) //10
}
test(&a)
可以看到这个指针就是指向a地址的指针
var a:Int = 10
func test(_ point:UnsafeMutablePointer<Int>) {
print(point) //0x000000010000c1f8
point.pointee = 20
print(point.pointee) //20
}
test(&a)
print(a) // 20
然后换成UnsafeMutablePointer就可以修改内容
再创建个类发现要改成UnsafeMutablePointer<Person>,而且还要往函数传值也很麻烦
extension Int
{
var pointer:UnsafePointer<Int>
{
mutating get{ withUnsafePointer(to: &self, { $0 }) }
}
}
var a:Int = 10
print(a.pointer) //0x000000010000c1f8
核心代码就这一句withUnsafePointer(to: &self, { $0 }) 其中{$0}是一个闭包(详见闭包),用处就是把第一个参数直接返回,这第一个参数就是指针,这个闭包里面可以自定义内容,一般来说,系统返回的指针就够用了
这样获取的都是变量本身的地址,而对于类来说,信息是存在堆空间的,而这个里面只是存储着那个的地址,所以就有了根据地址获取指针
var p:Person = Person()
let pointer = withUnsafePointer(to: &p, { UnsafeRawPointer($0) })
let cPointer = UnsafeMutableRawPointer(bitPattern: pointer.load(as: UInt.self))
print(cPointer)
至于这里为啥用UInt,原因就是这里获取的只是指向堆空间的指针的那个指针地址,这个地址实际上是Int类型的,堆空间的内容才是Person类型,为啥使用UnsafeRawPonter强制转换的原因也在这,因为从编译器里面看这个指针是Person类型的,而实际存储的值又是一个Int的地址值,所以无法用unsafePointer
所以可以得出结论,UnsafePointer系列的是常规指针,就是指向变量的地址,而要指向堆空间的对象指针,则需要用UnsafeRawPointer系列指针来完成