我正在参加「启航方案」

本文主要是对指针的认识,包含指针的根本运用,以及指针的内存绑定进行详细分析

主要内容:

  1. 指针的认识
  2. 指针的常见绑定

1. 指针的认识

指针分为两类,指定数据类型和未指定数据类型

差异:

Swift底层探索(五)Swift指针的详细认识

1.1 指定类型指针

代码:

Swift底层探索(五)Swift指针的详细认识

运行成果:

Swift底层探索(五)Swift指针的详细认识

阐明:

  • 指针的内存需求自己办理,需求手动开辟空间和开释空间
  • 存储数据时,需求移动必定的字节巨细
  • 移动经过advanced实现
  • 存储数据经过storeBytes实现
  • 取出数据经过load实现

1.2 未指定类型指针

代码:

<!--界说-->
@inlinablepublicfuncwithUnsafePointer<T,Result>(tovalue:inoutT,_body:(UnsafePointer<T>)throws->Result)rethrows->Result
<!--运用1-->
varage=10
letp=withUnsafePointer(to:&age){$0}
print(p)
<!--运用2-->
withUnsafePointer(to:&age){print($0)}
<!--运用3-->
//其间p1的类型是UnsafePointer<Int>
letp1=withUnsafePointer(to:&age){ptrin
returnptr
}

阐明:

  • 关于withUnsafePointer的界说,咱们能够看到闭包中回来的成果便是这个函数回来的成果
  • 因而咱们在运用这个指针时,就能够经过闭包回来数据来决定拿到的成果
  • 所以能够看到咱们能够有两种方法,1)直接回来指针;2)回来详细的数据

拜访特点:

直接修改:

直接在闭包中核算后将成果回来给特点

varage=10
age=withUnsafePointer(to:&age){ptrin
//回来Int整型值
returnptr.pointee+12
}
print(age)

直接修改:

varage=10
//分配容量巨细,为8字节
letptr=UnsafeMutablePointer<Int>.allocate(capacity:1)
//初始化
ptr.initialize(to:age)
ptr.deinitialize(count:1)
ptr.pointee+=12
print(ptr.pointee)
//开释
ptr.deallocate()

阐明:

  • 回来指针,指针拿到pointee来进行修改

1.3 拜访结构体实例目标

结构体:

structCJLTeacher{
varage=10
varheight=1.85
}
vart=CJLTeacher()

指针处理:

//分配两个CJLTeacher巨细的空间
letptr=UnsafeMutablePointer<CJLTeacher>.allocate(capacity:2)
//初始化第一个空间
ptr.initialize(to:CJLTeacher())
//移动,初始化第2个空间
ptr.successor().initialize(to:CJLTeacher(age:20,height:1.75))
//拜访方法一
print(ptr[0])
print(ptr[1])
//拜访方法二
print(ptr.pointee)
print((ptr+1).pointee)
//拜访方法三
print(ptr.pointee)
//successor往前移动
print(ptr.successor().pointee)
//有必要和分配是共同的
ptr.deinitialize(count:2)
//开释
ptr.deallocate()

阐明:

  • 直接经过下标来获取
  • 经过指针偏移来获取
  • 经过successor()偏移一步来获取,它能够通用在指定指针类型和未指定指针类型

2. 指针的常见绑定

2.1 指针与内存空间的绑定(指向)(bindMemory)

将指针指向某个内存空间,也便是绑定到这个内存空间上

界说:

structHeapObject{
varkind:Int
varstrongRef:UInt32
varunownedRef:UInt32
}
classCJLTeacher{
varage=18
}
vart=CJLTeacher()

绑定:

//将t绑定到结构体内存中
//1、获取实例变量的内存地址,声明成了非保管目标
/*
经过Unmanaged指定内存办理,类似于OC与CF的交互方法(所有权的转化__bridge)
-passUnretained不添加引证计数,即不需求获取所有权
-passRetained添加引证计数,即需求获取所有权
-toOpaque不透明的指针
*/
letptr=Unmanaged.passUnretained(tasAnyObject).toOpaque()
//2、绑定到结构体内存,回来值是UnsafeMutablePointer<T>
/*
-bindMemory更改当时UnsafeMutableRawPointer的指针类型,绑定到详细的类型值
-假如没有绑定,则绑定
-假如现已绑定,则重定向到HeapObject类型上
*/
letheapObject=ptr.bindMemory(to:HeapObject.self,capacity:1)
//3、拜访成员变量
print(heapObject.pointee.kind)
print(heapObject.pointee.strongRef)
print(heapObject.pointee.unownedRef)

阐明:

  1. 首要创立一个指针,指针指向CJLTeacher
  2. 经过bindMemeory将指针绑定到结构体HeapObject中
  3. 接下来就能够经过这个指针来拜访内存数据了

2.1 元组指针类型转化(假定内存绑定assumingMemoryBound)

元组和指针指向内存的数据类型不一样,就需求运用假定内存绑定

代码:

vartul=(10,20)
//UnsafePointer<T>
functestPointer(_p:UnsafePointer<Int>){
print(p)
}
withUnsafePointer(to:&tul){(tulPtr:UnsafePointer<(Int,Int)>)in
//不能运用bindMemory,由于现已绑定到详细的内存中了
//运用assumingMemoryBound,假定内存绑定,意图是告诉编译器ptr现已绑定过Int类型了,不需求再查看memory绑定
testPointer(UnsafeRawPointer(tulPtr).assumingMemoryBound(to:Int.self))
}

阐明:

  • testPointe需求传入的是一个泛型为Int的指针
  • 咱们此时想要传入一个元组,元组类型为(Int, Int),与Int类型不一样
  • 因而无法经过memoryBind来直接指向Int内存
  • 所以就需求经过assumingMemoryBound(to: Int.self)来指向
  • 这是由于假定内存绑定是假绑定,不需求进行严厉的类型查看

举例:获取结构体的特点的指针

structHeapObject{
varstrongRef:UInt32=10
varunownedRef:UInt32=20
}
functestPointer(_p:UnsafePointer<Int>){
print(p)
}
//实例化
vart=HeapObject()
//获取结构体特点的指针传入函数
withUnsafePointer(to:&t){(ptr:UnsafePointer<HeapObject>)in
//1. 获取变量
letstrongRef=UnsafeRawPointer(ptr)+MemoryLayout<HeapObject>.offset(of:\HeapObject.strongRef)!
//2. 传递strongRef特点的值
testPointer(strongRef.assumingMemoryBound(to:Int.self))
}

阐明:

  • 经过地址偏移拿到结构体中的strngRef变量
  • 之后也是经过假定绑定将其转化为一个指针(从Uint32到Int)

2.1 经过 withMemoryRebound 暂时绑定内存类型

问题:

Swift底层探索(五)Swift指针的详细认识

代码实现:

varage=10
functestPointer(_p:UnsafePointer<Int64>){
print(p)
}
letptr=withUnsafePointer(to:&age){$0}
ptr.withMemoryRebound(to:Int64.self,capacity:1){(ptr:UnsafePointer<Int64>)in
testPointer(ptr)
}

阐明:

  • 此处能够看到ptr的指针泛型为Int,而testPointer的参数指针泛型为Int64,所以并不能直接传递
  • 而这个指针咱们只是作为参数传递,所以就能够暂时绑定一下
  • 实现方法便是ptr.withMemoryRebound(to: Int64.self, capacity: 1)
  • 在出了这个作用域后,ptr仍然是Int类型

3、总结

  1. 指针类型分两种
    1. typed pointer 指定数据类型指针,即 UnsafePointer< T > + unsafeMutablePointer
    2. raw pointer 未指定数据类型的指针(原生指针) ,即UnsafeRawPointer + unsafeMutableRawPointer
  2. 指针的内存办理需求手动办理
  3. 假定内存绑定和内存绑定的差异
  4. 需求留意关于指针类型指针,能够经过指针偏移来偏移内存巨细,而关于未指定类型的指针,只能经过内存偏移来偏移内存巨细
  5. 将一个指针绑定到内存中,其实便是指向到这个内存空间
  6. 三种绑定的差异
    1. 绑定:bindMemory(to: Capacity:): 更改内存绑定的类型,假如之前没有绑定,那么便是初次绑定,假如绑定过了,会被重新绑定为该类型
    2. 假定绑定:assumingMemoryBound假定内存绑定,这儿便是告诉编译器:我的类型便是这个,你不要查看我了,其实践类型仍是原来的类型
    3. 暂时绑定:withMemoryRebound: 暂时更改内存绑定类型