swift内存管理指针类型使用实例详解
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>为什么说指针不安全</li><li>指针类型</li><li>原始指针-rawPointer 的使用</li><ul class="second_class_ul"><li>size/stride/alignment的理解</li><ul class="third_class_ul"><li>情况一</li><li>情况二</li></ul></ul><li>泛型指针的使用</li><ul class="second_class_ul"></ul></ul></div><p class="maodian"></p><h2>为什么说指针不安全</h2><ul><li>我们在创建一个对象的时候,是需要在堆上开辟内存空间的 但是这个内存空间的声明周期是有限的 也就意味着如果使用指针指向这块内存空间,当这块内存空间的生命周期结束(引用计数为0),那么当前的指针就变成未定义的了</li><li>创建的内存空间是有边界的,通过指针访问的内存空间超过已开辟内存空间的边界,也就是访问了一个未知的内存空间</li><li>指针类型与内存的值类型不一致,也不安全,这一点参考 swift指针&内存管理-内存绑定</li></ul>
<div class="cros igoods"><div class="goodsin" data-img="https://img14.360buyimg.com/pop/jfs/t1/222791/8/806/179453/617f6042E1dd43de9/1966134224a611a7.jpg" data-name="零基础iOS从入门到精通 ios书籍应用开发程序设计移动编程开发 Swift语言开发基础教程 应用开发教程 基础知识自学设计开发书籍" data-owner="京东自营" data-price="69.8" data-tgid="38" data-url="https://union-click.jd.com/jdc?e=&p=JF8BAMkJK1olXwUCVFxaDE4XBV8IGF0UXgcHVm4ZVxNJXF9RXh5UHw0cSgYYXBcIWDoXSQVJQwYBUl9eCU4VHDZNRwYlIwMYPVkJVR9yBydBUA5nBAF0AT1UeEcbM2gNHF4dXwMBZF5eDkwXAmoIK2sVXDZQOobrvpOysnPcsdTA1ZEyVW5dD0wfAGgIE1kWXA8DZF5VDHtUVypcWBhdbTYyV25tOEsnAF9WdVpGWwVQUl4KZhZHRzReT1gUMwYFXFlVCUkfM20JGlkXbTY"></div></div>
<p class="maodian"></p><h2>指针类型</h2>
<p>Swift中的指针分为两类 typed pointer(指定指针数据类型) & raw pointer(原生指针-未指定指针数据类型)</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202211/20221124085353014.png" /></p>
<p>如果需要开辟一段连续的内存空间,就可以使用 unsafeBufferPointer, 当然了unsafeMutableBufferPointer 就是可变的</p>
<p>连续的原生内存空间 unsafeRawBufferPointer / unsafeMutableRawBufferPointer , 这种指针需要结合 指针内存绑定来使用</p>
<p class="maodian"></p><h2>原始指针-rawPointer 的使用</h2>
<p>如何使用 rawPointer 来存储4个整型的数据</p>
<p>在存储之前,先了解几个概念</p>
<div class="jb51code"><pre class="brush:cpp;">print("MemoryLayout<Int>.size = \(MemoryLayout<Int>.size)")
print("MemoryLayout<Int>.stride = \(MemoryLayout<Int>.stride)")
print("MemoryLayout<Int>.alignment = \(MemoryLayout<Int>.alignment)")
print("MemoryLayout<Int32>.size = \(MemoryLayout<Int32>.size)")
print("MemoryLayout<Int32>.stride = \(MemoryLayout<Int32>.stride)")
print("MemoryLayout<Int32>.alignment = \(MemoryLayout<Int32>.alignment)")
print("MemoryLayout<Int16>.size = \(MemoryLayout<Int16>.size)")
print("MemoryLayout<Int16>.stride = \(MemoryLayout<Int16>.stride)")
print("MemoryLayout<Int16>.alignment = \(MemoryLayout<Int16>.alignment)")
</pre></div>
<p>结果:</p>
<blockquote><p>MemoryLayout.size = 8</p>
<p>MemoryLayout.stride = 8</p>
<p>MemoryLayout.alignment = 8</p>
<p>MemoryLayout.size = 4</p>
<p>MemoryLayout.stride = 4</p>
<p>MemoryLayout.alignment = 4</p>
<p>MemoryLayout.size = 2</p>
<p>MemoryLayout.stride = 2</p>
<p>MemoryLayout.alignment = 2</p></blockquote>
<p>MemoryLayout 是用来测定内存的</p>
<p>stride是步长,也就是一段连续内存空间 指定类型指针的偏移最小单位</p>
<p>alignment是对齐字节,一段连续内存空间,指令读取内存数据,都是标准化操作,不会出现第一个整型读了8字节,下一个整型读了4字节这样</p>
<p>然后我们进行 4个整型的数据的存储</p>
<p>首先开辟一块内存空间</p>
<blockquote><p>UnsafeMutableRawPointer.allocate(byteCount: Int, alignment: Int)</p></blockquote>
<p>byteCount: 开辟内存空间的总的字节大小</p>
<p>alignment: 连续内存空间中 每一个整型数据的对齐大小</p>
<blockquote><p>然后存储 - UnsafeMutableRawPointer - storeBytes(of: T, as: T.Type)</p></blockquote>
<p>of - 存储的数据</p>
<p>as - 存储的数据的类型</p>
<div class="jb51code"><pre class="brush:cpp;">let mP = UnsafeMutableRawPointer.allocate(
byteCount: 4 * MemoryLayout<Int>.size,
alignment: MemoryLayout<Int>.stride)
for i in 0..<4 {
mP.storeBytes(of: i, as: Int.self)
}
// 取出
for i in 0..<4 {
let mV = mP.load(as: Int.self)
let mV = mP.load(fromByteOffset: i * MemoryLayout<Int>.stride, as: Int.self)
print("mV ===> \(mV)")
}
</pre></div>
<p>结果</p>
<blockquote><p>mV ===> 3</p>
<p>mV ===> 3</p>
<p>mV ===> 3</p>
<p>mV ===> 3</p></blockquote>
<p>为什么不是 0, 1, 2, 3</p>
<p>这是因为 <code>mP</code> 指向 UnsafeMutableRawPointer.allocate 开辟出来的一段连续内存空间首地址</p>
<p>mP.load(as: Int.self) 循环里每次取的都是 从首地址处取出 数据,所以总是一样的3</p>
<p>调整之后</p>
<div class="jb51code"><pre class="brush:cpp;">let mP = UnsafeMutableRawPointer.allocate(
byteCount: 4 * MemoryLayout<Int>.size,
alignment: MemoryLayout<Int>.stride)
for i in 0..<4 {
mP.storeBytes(of: i, as: Int.self)
}
// 取出
for i in 0..<4 {
let mV = mP.load(fromByteOffset: i * MemoryLayout<Int>.stride, as: Int.self)
print("mV ===> \(mV)")
}
</pre></div>
<p>结果</p>
<blockquote><p>mV ===> 3</p>
<p>mV ===> 0</p>
<p>mV ===> 16</p>
<p>mV ===> 0</p></blockquote>
<p>这又是为何</p>
<p>因为 mP.storeBytes(of: i, as: Int.self) 每次也只是往 mP指向的连续内存空间的首地址里存储,所以最后存储的 3会覆盖前面的几次写值</p>
<div class="jb51code"><pre class="brush:cpp;">let mP = UnsafeMutableRawPointer.allocate(
byteCount: 4 * MemoryLayout<Int>.size,
alignment: MemoryLayout<Int>.stride)
for i in 0..<4 {
// 正解
mP.advanced(by: i * MemoryLayout<Int>.stride).storeBytes(of: i, as: Int.self)
}
// 取出
for i in 0..<4 {
let mV = mP.load(fromByteOffset: i * MemoryLayout<Int>.stride, as: Int.self)
print("mV ===> \(mV)")
}
</pre></div>
<p>结果</p>
<blockquote><p>mV ===> 0</p>
<p>mV ===> 1</p>
<p>mV ===> 2</p>
<p>mV ===> 3</p></blockquote>
<p>也可以直接 计算具体指针位置进行写值,前提是必须知道指针的类型才可以</p>
<div class="jb51code"><pre class="brush:cpp;">for i in 0..<4 {
(mP + i * MemoryLayout<Int>.stride).storeBytes(of: i, as: Int.self)
}
</pre></div>
<p class="maodian"></p><h3>size/stride/alignment的理解</h3>
<p class="maodian"></p><h4>情况一</h4>
<div class="jb51code"><pre class="brush:cpp;">struct IFLObject1 {
var age: Int
var gender: Bool
}
print("MemoryLayout<IFLObject1>.size = \(MemoryLayout<IFLObject1>.size)")
print("MemoryLayout<IFLObject1>.stride = \(MemoryLayout<IFLObject1>.stride)")
print("MemoryLayout<IFLObject1>.alignment = \(MemoryLayout<IFLObject1>.alignment)")
</pre></div>
<p>结果</p>
<blockquote><p>MemoryLayout.size = 9</p>
<p>MemoryLayout.stride = 16</p>
<p>MemoryLayout.alignment = 8</p></blockquote>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202211/20221124085353015.png" /></p>
<p class="maodian"></p><h4>情况二</h4>
<div class="jb51code"><pre class="brush:cpp;">class IFLobject2 {
var age: Int = 0
var gender: Bool = true
var heigh: Double = 170
var heigh2: Double = 170
var heigh3: Double = 170
var heigh4: Double = 170
}
print("MemoryLayout<IFLobject2>.size = \(MemoryLayout<IFLObject2>.size)")
print("MemoryLayout<IFLobject2>.stride = \(MemoryLayout<IFLObject2>.stride)")
print("MemoryLayout<IFLobject2>.alignment = \(MemoryLayout<IFLObject2>.alignment)")
</pre></div>
<p>结果</p>
<blockquote><p>MemoryLayout.size = 8</p>
<p>MemoryLayout.stride = 8</p>
<p>MemoryLayout.alignment = 8</p></blockquote>
<p>与结构体不同的是,struct属于值类型,栈上开辟空间,class 堆上开辟内存空间,指针大小为8字节, 所以8字节对齐,步长也是8字节</p>
<p class="maodian"></p><h2>泛型指针的使用</h2>
<p>泛型指针相比原生指针来说,就是当前指针绑定到了具体的类型</p>
<p>泛型指针访问过程中,并不是使用store load 方法进行存储 取值操作,而是使用到泛型指针内置的变量pointee</p>
<div class="jb51code"><pre class="brush:cpp;">var age = 10
var age1 = withUnsafePointer(to: &age) {
$0.pointee + 1
}
print("age1 = \(age1)")
</pre></div>
<p>结果</p>
<blockquote><p>age1 = 11</p></blockquote>
<p>另一种情况</p>
<div class="jb51code"><pre class="brush:cpp;">var age = 10
withUnsafePointer(to: &age) {
$0.pointee += 1
}
</pre></div>
<p>这种情况下 指针 0是不可变的,同时0 是不可变的,同时0是不可变的,同时0指向的内容 $0.pointee也是不可变的, 如果要操作,调整如下</p>
<div class="jb51code"><pre class="brush:cpp;">var age = 10
withUnsafeMutablePointer(to: &age) {
$0.pointee += 1
}
print("age = \(age)")
</pre></div>
<p>结果</p>
<blockquote><p>age = 11</p></blockquote>
<p>还有一种方式直接分配内存</p>
<div class="jb51code"><pre class="brush:cpp;">var age = 10
let tPtr = UnsafeMutablePointer<Int>.allocate(capacity: 1)
tPtr.initialize(to: age)
print(tPtr.pointee)
</pre></div>
<p>结果</p>
<blockquote><p>10</p></blockquote>
<div class="jb51code"><pre class="brush:cpp;">struct IFLObjStruct {
var age: Int
var height: Double
}
var tPtr = UnsafeMutablePointer<IFLObjStruct>.allocate(capacity: 5)
tPtr = IFLObjStruct(age: 19, height: 170.0)
tPtr = IFLObjStruct(age: 20, height: 171.0)
tPtr = IFLObjStruct(age: 21, height: 172.0)
tPtr = IFLObjStruct(age: 22, height: 173.0)
tPtr = IFLObjStruct(age: 23, height: 174.0)
print(tPtr)
</pre></div>
<p>结果</p>
<blockquote><p>IFLObjStruct(age: 23, height: 174.0)</p></blockquote>
<p>还可以</p>
<div class="jb51code"><pre class="brush:cpp;">struct IFLObjStruct {
var age: Int
var height: Double
}
var tPtr = UnsafeMutablePointer<IFLObjStruct>.allocate(capacity: 5)
tPtr = IFLObjStruct(age: 19, height: 170.0)
tPtr = IFLObjStruct(age: 20, height: 171.0)
tPtr = IFLObjStruct(age: 21, height: 172.0)
tPtr = IFLObjStruct(age: 22, height: 173.0)
tPtr = IFLObjStruct(age: 23, height: 174.0)
tPtr.deinitialize(count: 5)
// 回收内存空间
tPtr.deallocate()
tPtr = UnsafeMutablePointer<IFLObjStruct>.allocate(capacity: 5)
for i in 0..<5 {
tPtr.advanced(by: i).initialize(to: IFLObjStruct(age: 19 + i * 5, height: 170.0 + Double(i * 5)))
}
for i in 0..<5 {
print(tPtr.advanced(by: i).pointee)
}
</pre></div>
<p>结果</p>
<blockquote><p>IFLObjStruct(age: 19, height: 170.0)</p>
<p>IFLObjStruct(age: 24, height: 175.0)</p>
<p>IFLObjStruct(age: 29, height: 180.0)</p>
<p>IFLObjStruct(age: 34, height: 185.0)</p>
<p>IFLObjStruct(age: 39, height: 190.0)</p></blockquote>
<p>注意:</p>
<p>tPtr.advanced by 参数 含义是 只需要标明移动多少个指针内存单位, 并不需要计算具体移动的内存块字节大小,</p>
<p>因为 泛型指针已经 指明了当前内存 绑定的具体类型, 与原生指针 adviced by 参数有所区别</p>
<p>一般情况下,我们会在 defer 中,也就是当前程序运行完成之后, 执行 deinitialize 与 deallocate</p>
<p>以上就是swift内存管理指针类型使用实例详解的详细内容,更多关于swift内存管理指针类型的资料请关注琼殿技术社区其它相关文章!</p>
<div class="art_xg">
<b>您可能感兴趣的文章:</b><ul><li>SwiftUI开发总结combine原理简单示例详解</li><li>仓库模式及其在Swift 项目中的应用详解</li><li>swift依赖注入和依赖注入容器详解</li><li>Framework中实现OC和Swift的混编方案</li><li>Swift设计思想Result<T>与Result<T, E: Error>类型解析</li><li>Swift Error重构优化详解</li><li>Swift Error重构的基础示例详解</li><li>Swift中的HTTP请求体Request Bodies使用示例详解</li></ul>
</div>
</div>
<!--endmain-->
頁:
[1]