美文网首页
Swift-指针

Swift-指针

作者: Mr木子李 | 来源:发表于2022-01-17 17:20 被阅读0次

swift中的指针分为两类

  • typed pointer 指定数据类型指针,即 UnsafePointer<T>,其中T表示泛型
  • raw pointer 未指定数据类型的指针(原生指针),即UnsafeRawPointer
    swiftOC指针对比如下:
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的使用

对于泛型指针,其实就是原生指针制定了指针类型,因为确定了类型,就固定了存储类型的 stridealignment,我们就不需要通过 loadstore 的方式存取,而是通过其内置的 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);
    }
}

相关文章

  • MemoryLayout理解

    swift-指针类型Memory Layout的简介与使用 https://blog.csdn.net/die_w...

  • Swift-指针

    swift中的指针分为两类 typed pointer 指定数据类型指针,即 UnsafePointer,其...

  • Swift-指针&内存管理

    石墨文档编写 《Swift-指针&内存管理》[https://shimo.im/docs/c3x9rWcR8YQj...

  • Swift-进阶 04:指针

    Swift 进阶之路 文章汇总[https://www.jianshu.com/p/5fbedf309237] S...

  • Swift-进阶 04:指针

    本文主要介绍swift中的指针 swift中的指针分为两类 typed pointer 指定数据类型指针,即 Un...

  • Swift-内存管理,指针

    内存管理 Swift采用引用计数的ARC内存管理方案(堆空间) Swift的ARC中有3钟引用强引用弱引用(wea...

  • Swift-进阶 04:指针

    Swift 进阶之路 文章汇总[https://www.jianshu.com/writer#/notebooks...

  • 【四】Swift-指针&内存管理

    目录 一、指针 1.为什么说指针是不安全的 2.指针类型 3.原始指针的使用 4.泛型指针的使用 5.内存绑定 二...

  • swift-类属性

    了解属性之前,需要先了解前面的swift-类结构内容 - swift-类结构源码探寻[https://www.ji...

  • Swift4.0 --- 第一节:变量和常量

    // // ViewControllerOne.swift // Swift-(1) // // Created ...

网友评论

      本文标题:Swift-指针

      本文链接:https://www.haomeiwen.com/subject/gzdecrtx.html