本文主要介绍Swift的指针类型,本质也是地址,但是Swift提供专门的指针类型存储地址。
主要内容:
- 指针类型的认识
- 指针类型的使用
- 类型转换
1、指针认识
Swift中也有专门的指针类型,这些都被定性为“Unsafe”(不安全的),Swift中并不会简单的认为地址就是一个指针,而是有专门的类型进行包装。只要获取到地址就可以对数据进行无访问限制的操作,因此是不安全的。
指针类型:
指针类型 | 认识 |
---|---|
UnsafePointer< Pointee > | 类似于 const Pointee * |
UnsafeMutablePointer< Pointee > | 类似于 Pointee * |
UnsafeRawPointer | 类似于 const void * |
UnsafeMutableRawPointer | 类似于 void * |
说明:
1、指针都是不安全的,因此都是Unsafe
2、没有Mutalbe的表示仅可读指针指向的内存,带有Mutable表示可读可写
3、< Pointee >是泛型,表示指针的类型
4、带Raw的都是不支持泛型的,所以都是void类型,类型不定
2、指针的使用
2.1 带泛型
代码:
说明:
1、通过指针的pointee来拿到内存数据
2、依然是取地址符拿到指针,只不过需要存储到Swift提供的指针类型中
3、注意Mutable的可以进行修改
2.2 无泛型
代码:
说明:
1、没有设置类型,需要自己设置一下类型
2、注意赋值为load和取值为storeBytes即可
2.3 通过指针遍历数组
代码:
var arr = NSArray(objects: 11, 22, 33, 44)
arr.enumerateObjects { (obj, idx, stop) in
print(idx, obj)
if idx == 2 { // 下标为2就停止遍历
stop.pointee = true//指针赋值
}
}
说明:
- 这种遍历方式中stop参数其实就是一个指针类型()
- 指针拿到自己的pointee就可以进行修改了。
3、获取指针变量
3.1 获取变量的指针
3.1.1 拿到带泛型的指针
var age = 11
//带泛型指针
var ptr1 = withUnsafeMutablePointer(to: &age) { $0 }
var ptr2 = withUnsafePointer(to: &age) { $0 }
ptr1.pointee = 22
print(ptr2.pointee) // 22
print(age) // 22
说明:
- with开头的可以获取,最后一个参数是闭包表达式
withUnsafeMutablePointer方法认识:
@inlinable public func withUnsafeMutablePointer<T, Result>(to value: inout T, _ body: (UnsafeMutablePointer<T>) throws -> Result) rethrows -> Result
说明:
1、第一个参数传的就是指针
2、第二个参数是一个闭包表达式,获取最终的Result
3、闭包表达式的参数其实就是传入的指针
4、并且可以看到闭包表达式返回的东西就是这个withUnsafePointer返回的东西
3.1.2 拿到无泛型的指针
//无泛型指针
var ptr3 = withUnsafeMutablePointer(to: &age) { UnsafeMutableRawPointer($0) }
var ptr4 = withUnsafePointer(to: &age) { UnsafeRawPointer($0) }
ptr3.storeBytes(of: 33, as: Int.self)
print(ptr4.load(as: Int.self)) // 33
print(age) // 33
说明:
- 这里是通过无泛型指针的初始化器来设置的,传入的就是带泛型的指针,所以可以直接传入$0
3.1.3 拿到变量的指针
代码:
说明:
- 这里很明显ptr拿到的是person变量的地址值。指针的本意嘛,不用多言。
3.1.4 拿到对象的指针
方式一:直接获取对象地址作为指针
class Person {
var age: Int
init (age: Int) {
self.age = age
}
}
var person = Person(age: 18)
var ptr = withUnsafePointer(to: &person) { $0 }
print("变量指针:",ptr)
var personPointer = UnsafeMutableRawPointer?(bitPattern: ptr)
print("对象指针:",personPointer)
说明:
1、调用bitPattern初始化器,里面传入的是对象的地址,此时就可以将该地址包装成指针,也就是放到一个全局区/栈
2、因为传入的参数不一定是正确的,所以是可失败的初始化器
方式二:通过变量获取变量内容作为指针
说明:
1、这里传入person变量,unsafeBitCast函数就可以拿到这个变量的内容赋值给ptr
2、并且设置的类型就是UnsafeRawPointer。
3、通过这种方式就可以很方便的得到对象的堆空间地址
3.2 创建一个无指向的指针
方式一:
//方式一:
var ptr = malloc(16)
ptr?.storeBytes(of: 10, as: Int.self)
ptr?.storeBytes(of: 20, toByteOffset: 8, as: Int.self)
free(ptr)//释放内存
说明:
1、malloc创建一个空间,此时拿到的ptr的类型为:(注意肯定为可选项)
2、按照正常的添加流程进行添加。注意总共16个字节,一次性赋值只赋给了前8个字节。所以还需要再赋后8个字节,toByteOffset是偏移量
方式二:
//方式二:
var ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
ptr.storeBytes(of: 11, as: Int.self)
ptr.advanced(by: 8).storeBytes(of: 22, as: Int.self)
print(ptr.load(as: Int.self)) // 11
print(ptr.advanced(by: 8).load(as: Int.self)) // 22
ptr.deallocate()
说明:
1、通过allocate进行创建,填入字节数和对齐数
2、advanced(by: 8)是将ptr指针偏移8个字节,并且返回一个指针。所以它得到的就是后8个字节的指针
3、最后需要通过deallcate销毁
方式三:
//方式三:
var ptr = UnsafeMutablePointer<Int>.allocate(capacity: 3)
ptr.initialize(to: 11)
ptr.successor().initialize(to: 22)
ptr.successor().successor().initialize(to: 33)
print(ptr.pointee) // 11
print((ptr + 1).pointee) // 22
print((ptr + 2).pointee) // 33
print(ptr[0]) // 11
print(ptr[1]) // 22
print(ptr[2]) // 33
ptr.deinitialize(count: 3)
ptr.deallocate()
说明:
1、如果带有泛型创建指针,那么可以直接设置容量,这里的容量是多少个值,而非字节数
2、在设置值时,可以直接用initialize(repeating: ,count:)重复设置两个值,每个都是10
3、也可以用initialize()只设置第一个值
4、ptr.successor()的作用就是偏移8个字节拿到其指针
5、(ptr + 1)是指针偏移,直接偏移8个字节
6、这里ptr+1、ptr[1]等价的
7、最后需要销毁内存
注意:无泛型指针会进行字节偏移,而不是指针偏移:如果是泛型指针,因为已经知道占用内存大小了,所以是可以进行指针偏移的,如果是非泛型指针,不能进行指针偏移,因为不知道一次性偏移多少
4、类型转换
第一种:非泛型指针的初始化:
var ptr = withUnsafeMutablePointer(to: &age) { UnsafeMutableRawPointer($0) }
var ptr2 = withUnsafePointer(to: &age) { UnsafeRawPointer($0) }
说明:
1、泛型指针转非泛型指针可以通过非泛型指针的初始化器来设置
第二种:assumingMemoryBound:
说明:
1、非泛型指针转泛型指针可以通过这个方法来转
第三种:unsafeBitCast:
转换时会忽略数据类型的强制转换,不会因为数据类型的变化而改变原来的内存数据
说明:
- 正常的数据类型转换,会改变存储的数据
- 通过unsafeBitCast的转换不会改变内存数据