swift
中的指针分为两类
-
typed pointer
指定数据类型指针,即UnsafePointer<T>
,其中T
表示泛型 -
raw pointer
未指定数据类型的指针(原生指针),即UnsafeRawPointer
swift
与OC
指针对比如下:
swift | OC | 说明 |
---|---|---|
unsafePointer<T> | const T * | 指针及所指向的内容都不可变 |
unsafeMutablePointer | T * | 指针及其所指向的内存内容均可变 |
unsafeRawPointer | const void * | 指针指向未知类型 |
unsafeMutableRawPointer | void * | 指针指向未知类型 |
说明
第一行 指针所指向的内存都不可变,但是有一点区别是:
let UnsafePointer<T> == const T *指针不可改变
var UnsafePointer<T> 指针可以改变的
指针使用需要注意的点
所有的指针有 unsafe官方已经在极力的提醒我们,指针是不安全的,可以通过以下几点来理解
- 野指针
创建对象的时候需要在对空间分配内存,但是这块内存其实是有生命周期的,当我使用指针来指向这个内存的时候,有可能该内存的引用计数为0 ,也就是被销毁了,那么指针指向的时候就是一个位定义的行为,也就是野指针形成
- 越界
系统在分配内存的时候是有一定大小的,比如数组的大小是 10 ,如果通过指针访问了 index 为 11 的位置则会访问到未知的内存空间
- 类型不同
系统分配的内存空间都是有一定类型的,指针的类型可能和内存空间值的类型不一致,这也是不安全 ,比如 一个Int8 类型的指针 指向了一个 Int类型的数据,就可能会精度缺失
原生指针raw pointer
的使用
在使用Swift
指针时,内存管理需要手动管理,也就是创建的指针在最后需要调用deallocate
使用指针最终还是要指向,我们创建的各种类型,所以先要了解内存布局方式的三个重要信息
size
实际需要的大小
stride
步长,连续存储多个元素指针间的间隔,也可以理解为系统最终分配的大小
alignment
内存对齐的基数,最终分配的内存空间大小是它的倍数
常用API
创建指针
/// - byteCount: 需要多少内存空间
/// - alignment:内存对齐的基数,按照它的倍数对器
allocate(byteCount:, alignment:)
存储数据
// 移动指针到下一个存储的位置
// - by: 移动的步长 确定类型则直接移动步数
advanced(by:)
//存储数据
// - of: 数据值
// - as: 数据类型
.storeBytes(of: , as: )
读取数据
// - fromByteOffset: 读取数据移动的步长
// - as: 数据类型
load(fromByteOffset: , as: )
销毁空间
deallocate()
案例:通过指针存储四个 Int 值
//原生指针
//对于指针的内存管理是需要手动管理的
//定义一个未知类型的指针:本质是分配32字节大小的空间,指定对齐方式是8字节对齐
let p = UnsafeMutableRawPointer.allocate(byteCount: 32, alignment: 8)
//存储
for i in 0..<5 {
//指定当前移动的步数,即i * 8
p.advanced(by: i * 8).storeBytes(of: i + 1, as: Int.self)
}
//读取
for i in 0..<4 {
//p是当前内存的首地址,通过内存平移来获取值
let value = p.load(fromByteOffset: i * 8, as: Int.self)
print("index: \(i), value: \(value)")
}
//使用完成需要dealloc,即需要手动释放
p.deallocate()
指定数据类型指针type pointer
的使用
对于泛型指针,其实就是原生指针制定了指针类型,因为确定了类型,就固定了存储类型的 stride
和alignment
,我们就不需要通过 load
和 store
的方式存取,而是通过其内置的 pointee
变量来存取
常用API
创建指针
1.直接分配内存的方式
//此时的内存空间没有被初始化,需要初始化内存
// - Parameter count: 分配多少个 范型 大小的连续空间
allocate(capacity count:)
//- Parameter value: 初始化到内存空间内 只能初始化 一个步长的大小
initialize(to value:)
2.通过已有变量获取
// - value: 初始化的值.
// - body: 可以操作指针的闭包表达式,withUnsafePointer<T>,无法在闭包中修改 pointee
// 返回值就是 body 的返回值
withUnsafePointer<T, Result>(to value: T, _ body: (UnsafePointer<T>)
// - value: 初始化的值.
// - body: 可以操作指针的闭包表达式,withUnsafePointer<T>,可以在闭包中修改
pointee
// 返回值就是 body 的返回值
withUnsafeMutablePointer<T, Result>(to value: inout T, _ body: (UnsafeMutablePointer<T>)
访问内存空间的方式
var ptr = UnsafeMutablePointer<Int>.allocate(capacity: 5)
1. 下标访问
ptr[0] = 1
ptr[1] = 2
2. 内存平移
销毁空间
//与 initialize 成对出现
// coun 与 initialize 相同
allocate(count: Int)
deallocate()
案例:访问结构体实例对象
//定义一个结构体
class Person {
var age: Int = 18
var name: String = "LG-Kody"
}
var size: UInt = 0
//__swift5_types section 的pFile
var typesPtr = getsectdata("__TEXT", "__swift5_types", &size)
// 获取当前程序运行地址 相当于 LLDB 中 image list 命令
var excute_header: UnsafePointer<mach_header>?
let count = _dyld_image_count()
print("count \(count)")
for i in 0..<count{
let mhHeaderPtr = _dyld_get_image_header(i)
if mhHeaderPtr!.pointee.filetype == MH_EXECUTE {
excute_header = mhHeaderPtr
}
}
print("excute_header \(excute_header)")
var mhHeaderPtr = excute_header
//print("mhHeaderPtr \(mhHeaderPtr)")
// 获取 __LINKEDIT 中的内容 其中 getsegbyname 返回的是 UnsafePointer<segment_command_64>, segment_command_64 就包含了 vmaddr(虚拟内存地址) 和 fileoff(偏移量)
var setCommond64LinkeditPtr = getsegbyname("__LINKEDIT")
// 10 : 4294967296 - > 16: 0x100000000
//// 计算链接的基地址
var linkBaseAddress: UInt64 = 0
//if let vmaddr = setCommond64LinkeditPtr?.pointee.vmaddr, let fileOff = setCommond64LinkeditPtr?.pointee.fileoff{
// print("vmaddr \(vmaddr)")
// print("fileOff \(fileOff)")
// linkBaseAddress = vmaddr - fileOff
//}
if let vmaddr = setCommond64LinkeditPtr?.pointee.vmsize{
linkBaseAddress = vmaddr
}
print("linkBaseAddress \(linkBaseAddress)")
// 或者 直接去 LC_SEGMENT_64(__PAGEZERO)中的VM Size
var setCommond64PageZeroPtr = getsegbyname("__PAGEZERO")
if let vmsize = setCommond64PageZeroPtr?.pointee.vmsize {
linkBaseAddress = vmsize
}
// 获取__TEXT, __swift5_types 在Macho中的偏移量
var typesOffSet: UInt64 = 0
if let unwrappedPtr = typesPtr {
// 将当前的地址信息转换成UInt64
let intRepresentation = UInt64(bitPattern: Int64(Int(bitPattern: unwrappedPtr)))
print("intRepresentation \(intRepresentation)")
typesOffSet = intRepresentation - linkBaseAddress
}
// BCE8
print("typesOffSet = \(typesOffSet)")
// 程序运行的首地址 转换成UInt64类型
let mhHeaderPtr_IntRepresentation = UInt64(bitPattern: Int64(Int(bitPattern: mhHeaderPtr)))
print("mhHeaderPtr_IntRepresentation \(mhHeaderPtr_IntRepresentation)")
// DataLo的内存地址
var dataLoAddress = mhHeaderPtr_IntRepresentation + typesOffSet
print("dataLoAddress \(dataLoAddress)")
// 转换成指针类型
var dataLoAddressPtr = withUnsafePointer(to: &dataLoAddress){return $0}
print("dataLoAddressPtr \(dataLoAddressPtr)")
// 获取dataLo指针指向的内容
var dataLoContent = UnsafePointer<UInt32>.init(bitPattern: Int(exactly: dataLoAddress) ?? 0)?.pointee
// FF FF FB F0
print("dataLoContent \(dataLoContent)")
// 获取typeDescriptor的偏移量
let typeDescOffset = UInt64(dataLoContent!) + typesOffSet - linkBaseAddress
// 获取typeDescriptor在程序运行中的地址
var typeDescAddress = typeDescOffset + mhHeaderPtr_IntRepresentation
// 0x10000B8D8
print("typeDescAddress \(typeDescAddress)")
// typeDescriptor结构体
struct TargetClassDescriptor{
var flags: UInt32
var parent: UInt32
var name: Int32
var accessFunctionPointer: Int32
var fieldDescriptor: Int32
var superClassType: Int32
var metadataNegativeSizeInWords: UInt32
var metadataPositiveSizeInWords: UInt32
var numImmediateMembers: UInt32
var numFields: UInt32
var fieldOffsetVectorOffset: UInt32
var Offset: UInt32
var methods: UInt32
}
// 将 typeDescriptor 的内存地址直接转换成指向 TargetClassDescriptor 结构体的指针
let classDescriptor = UnsafePointer<TargetClassDescriptor>.init(bitPattern: Int(exactly: typeDescAddress) ?? 0)?.pointee
if let name = classDescriptor?.name {
print("name \(name)")
// 获取name的偏移量地址
let nameOffset = Int64(name) + Int64(typeDescOffset) + 8
// 获取name在运行中的内存地址
let nameAddress = nameOffset + Int64(mhHeaderPtr_IntRepresentation)
if let cChar = UnsafePointer<CChar>.init(bitPattern: Int(nameAddress)){
print(String(cString: cChar))
}
}
// 获取属性
// 获取属性相关的filedDescriptor 在运行中的内存地址
let filedDescriptorRelaticveAddress = typeDescOffset + 4 * 4 + mhHeaderPtr_IntRepresentation
struct FieldDescriptor {
var mangledTypeName: Int32
var superclass: Int32
var Kind: UInt16
var fieldRecordSize: UInt16
var numFields: UInt32
var fieldRecords: [FieldRecord]
}
struct FieldRecord{
var Flags: UInt32
var mangledTypeName: Int32
var fieldName: UInt32
}
// 获取fieldDescriptor 指针在的内容 就是FieldDescriptor 的偏移量
let fieldDescriptorOffset = UnsafePointer<UInt32>.init(bitPattern: Int(exactly: filedDescriptorRelaticveAddress) ?? 0)?.pointee
print("fieldDescriptorOffset \(fieldDescriptorOffset)")
// 获取 FieldDescriptor 的在运行中的内存地址
let fieldDescriptorAddress = filedDescriptorRelaticveAddress + UInt64(fieldDescriptorOffset!)
// 将 FieldDescriptor 的内存地址直接转换成指向 FieldDescriptor 结构体的指针
let fieldDescriptor = UnsafePointer<FieldDescriptor>.init(bitPattern: Int(exactly: fieldDescriptorAddress) ?? 0)?.pointee
// 循环遍历属性
for i in 0..<fieldDescriptor!.numFields{
// FieldRecord 结构体由 3个 4字节组成,并且保持3 * 4 = 12字节对齐
let stride: UInt64 = UInt64(i * 3 * 4)
// 计算 fieldRecord 的地址
let fieldRecordAddress = fieldDescriptorAddress + stride + 16
// 计算 fieldRecord 结构体中的 name 在程序运行中的内存地址
let fieldNameRelactiveAddress = UInt64(2 * 4) + fieldRecordAddress - linkBaseAddress + mhHeaderPtr_IntRepresentation
// 将上面地址的地址转换成指针,并且获取指向的内容 (偏移量)
let nameOffset = UnsafePointer<UInt32>.init(bitPattern: Int(exactly: fieldNameRelactiveAddress) ?? 0)?.pointee
// 获取 name 的地址
let fieldNameAddress = fieldNameRelactiveAddress + UInt64(nameOffset!) - linkBaseAddress
// 将 name 地址转换成指针
if let cChar = UnsafePointer<CChar>.init(bitPattern: Int(fieldNameAddress)){
// 打印指针内容
print(String(cString: cChar))
}
}
// 获取v-table
// 函数的结构体
struct TargetMethodDescriptor {
var kind: UInt32
var offset: UInt32
}
// 获取方法的数量
if let methods = classDescriptor?.methods {
for i in 0..<methods {
// 获取v-table的的首地址
let VTableRelaticveAddress = typeDescOffset + 4 * 13 + mhHeaderPtr_IntRepresentation
// 获取当前函数的地址
let currentMethodAddress = VTableRelaticveAddress + UInt64(i) * UInt64(MemoryLayout<TargetMethodDescriptor>.size)
// 将 当前函数 的内存地址直接转换成指向 TargetMethodDescriptor 结构体的指针
let currentMethod = UnsafePointer<TargetMethodDescriptor>.init(bitPattern: Int(exactly: currentMethodAddress) ?? 0)?.pointee
// 获取到imp的地址
let impAddress = currentMethodAddress + 4 + UInt64(currentMethod!.offset) - linkBaseAddress
print(impAddress);
}
}
网友评论