網(wǎng)站首頁 編程語言 正文
為什么說指針不安全
- 我們在創(chuàng)建一個(gè)對(duì)象的時(shí)候,是需要在堆上開辟內(nèi)存空間的 但是這個(gè)內(nèi)存空間的聲明周期是有限的 也就意味著如果使用指針指向這塊內(nèi)存空間,當(dāng)這塊內(nèi)存空間的生命周期結(jié)束(引用計(jì)數(shù)為0),那么當(dāng)前的指針就變成未定義的了
- 創(chuàng)建的內(nèi)存空間是有邊界的,通過指針訪問的內(nèi)存空間超過已開辟內(nèi)存空間的邊界,也就是訪問了一個(gè)未知的內(nèi)存空間
- 指針類型與內(nèi)存的值類型不一致,也不安全,這一點(diǎn)參考 swift指針&內(nèi)存管理-內(nèi)存綁定
指針類型
Swift中的指針分為兩類 typed pointer(指定指針數(shù)據(jù)類型) & raw pointer(原生指針-未指定指針數(shù)據(jù)類型)
如果需要開辟一段連續(xù)的內(nèi)存空間,就可以使用 unsafeBufferPointer, 當(dāng)然了unsafeMutableBufferPointer 就是可變的
連續(xù)的原生內(nèi)存空間 unsafeRawBufferPointer / unsafeMutableRawBufferPointer , 這種指針需要結(jié)合 指針內(nèi)存綁定來使用
原始指針-rawPointer 的使用
如何使用 rawPointer 來存儲(chǔ)4個(gè)整型的數(shù)據(jù)
在存儲(chǔ)之前,先了解幾個(gè)概念
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)")
結(jié)果:
MemoryLayout.size = 8
MemoryLayout.stride = 8
MemoryLayout.alignment = 8
MemoryLayout.size = 4
MemoryLayout.stride = 4
MemoryLayout.alignment = 4
MemoryLayout.size = 2
MemoryLayout.stride = 2
MemoryLayout.alignment = 2
MemoryLayout 是用來測定內(nèi)存的
stride是步長,也就是一段連續(xù)內(nèi)存空間 指定類型指針的偏移最小單位
alignment是對(duì)齊字節(jié),一段連續(xù)內(nèi)存空間,指令讀取內(nèi)存數(shù)據(jù),都是標(biāo)準(zhǔn)化操作,不會(huì)出現(xiàn)第一個(gè)整型讀了8字節(jié),下一個(gè)整型讀了4字節(jié)這樣
然后我們進(jìn)行 4個(gè)整型的數(shù)據(jù)的存儲(chǔ)
首先開辟一塊內(nèi)存空間
UnsafeMutableRawPointer.allocate(byteCount: Int, alignment: Int)
byteCount: 開辟內(nèi)存空間的總的字節(jié)大小
alignment: 連續(xù)內(nèi)存空間中 每一個(gè)整型數(shù)據(jù)的對(duì)齊大小
然后存儲(chǔ) - UnsafeMutableRawPointer - storeBytes(of: T, as: T.Type)
of - 存儲(chǔ)的數(shù)據(jù)
as - 存儲(chǔ)的數(shù)據(jù)的類型
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)") }
結(jié)果
mV ===> 3
mV ===> 3
mV ===> 3
mV ===> 3
為什么不是 0, 1, 2, 3
這是因?yàn)?mP
指向 UnsafeMutableRawPointer.allocate 開辟出來的一段連續(xù)內(nèi)存空間首地址
mP.load(as: Int.self) 循環(huán)里每次取的都是 從首地址處取出 數(shù)據(jù),所以總是一樣的3
調(diào)整之后
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)") }
結(jié)果
mV ===> 3
mV ===> 0
mV ===> 16
mV ===> 0
這又是為何
因?yàn)?mP.storeBytes(of: i, as: Int.self) 每次也只是往 mP指向的連續(xù)內(nèi)存空間的首地址里存儲(chǔ),所以最后存儲(chǔ)的 3會(huì)覆蓋前面的幾次寫值
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)") }
結(jié)果
mV ===> 0
mV ===> 1
mV ===> 2
mV ===> 3
也可以直接 計(jì)算具體指針位置進(jìn)行寫值,前提是必須知道指針的類型才可以
for i in 0..<4 { (mP + i * MemoryLayout<Int>.stride).storeBytes(of: i, as: Int.self) }
size/stride/alignment的理解
情況一
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)")
結(jié)果
MemoryLayout.size = 9
MemoryLayout.stride = 16
MemoryLayout.alignment = 8
情況二
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)")
結(jié)果
MemoryLayout.size = 8
MemoryLayout.stride = 8
MemoryLayout.alignment = 8
與結(jié)構(gòu)體不同的是,struct屬于值類型,棧上開辟空間,class 堆上開辟內(nèi)存空間,指針大小為8字節(jié), 所以8字節(jié)對(duì)齊,步長也是8字節(jié)
泛型指針的使用
泛型指針相比原生指針來說,就是當(dāng)前指針綁定到了具體的類型
泛型指針訪問過程中,并不是使用store load 方法進(jìn)行存儲(chǔ) 取值操作,而是使用到泛型指針內(nèi)置的變量pointee
var age = 10 var age1 = withUnsafePointer(to: &age) { $0.pointee + 1 } print("age1 = \(age1)")
結(jié)果
age1 = 11
另一種情況
var age = 10 withUnsafePointer(to: &age) { $0.pointee += 1 }
這種情況下 指針 0是不可變的,同時(shí)0 是不可變的,同時(shí)0是不可變的,同時(shí)0指向的內(nèi)容 $0.pointee也是不可變的, 如果要操作,調(diào)整如下
var age = 10 withUnsafeMutablePointer(to: &age) { $0.pointee += 1 } print("age = \(age)")
結(jié)果
age = 11
還有一種方式直接分配內(nèi)存
var age = 10 let tPtr = UnsafeMutablePointer<Int>.allocate(capacity: 1) tPtr.initialize(to: age) print(tPtr.pointee)
結(jié)果
10
struct IFLObjStruct { var age: Int var height: Double } var tPtr = UnsafeMutablePointer<IFLObjStruct>.allocate(capacity: 5) tPtr[0] = IFLObjStruct(age: 19, height: 170.0) tPtr[1] = IFLObjStruct(age: 20, height: 171.0) tPtr[2] = IFLObjStruct(age: 21, height: 172.0) tPtr[3] = IFLObjStruct(age: 22, height: 173.0) tPtr[4] = IFLObjStruct(age: 23, height: 174.0) print(tPtr[4])
結(jié)果
IFLObjStruct(age: 23, height: 174.0)
還可以
struct IFLObjStruct { var age: Int var height: Double } var tPtr = UnsafeMutablePointer<IFLObjStruct>.allocate(capacity: 5) tPtr[0] = IFLObjStruct(age: 19, height: 170.0) tPtr[1] = IFLObjStruct(age: 20, height: 171.0) tPtr[2] = IFLObjStruct(age: 21, height: 172.0) tPtr[3] = IFLObjStruct(age: 22, height: 173.0) tPtr[4] = IFLObjStruct(age: 23, height: 174.0) tPtr.deinitialize(count: 5) // 回收內(nèi)存空間 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) }
結(jié)果
IFLObjStruct(age: 19, height: 170.0)
IFLObjStruct(age: 24, height: 175.0)
IFLObjStruct(age: 29, height: 180.0)
IFLObjStruct(age: 34, height: 185.0)
IFLObjStruct(age: 39, height: 190.0)
注意:
tPtr.advanced by 參數(shù) 含義是 只需要標(biāo)明移動(dòng)多少個(gè)指針內(nèi)存單位, 并不需要計(jì)算具體移動(dòng)的內(nèi)存塊字節(jié)大小,
因?yàn)?泛型指針已經(jīng) 指明了當(dāng)前內(nèi)存 綁定的具體類型, 與原生指針 adviced by 參數(shù)有所區(qū)別
一般情況下,我們會(huì)在 defer 中,也就是當(dāng)前程序運(yùn)行完成之后, 執(zhí)行 deinitialize 與 deallocate
原文鏈接:https://juejin.cn/post/7169234917270749215
相關(guān)推薦
- 2022-04-18 2.* 版本taro引入 taro-ui編譯小程序階段報(bào)錯(cuò), Module not found: C
- 2022-09-06 Django的restframework接口框架自定義返回?cái)?shù)據(jù)格式的示例詳解_python
- 2023-01-18 C#實(shí)現(xiàn)圖片輪播功能的示例代碼_C#教程
- 2024-03-17 WSL子系統(tǒng)啟動(dòng)報(bào)錯(cuò) Wsl/Service/CreateInstance/CreateVm/HCS
- 2022-05-13 修復(fù)ffmpeg寫文件時(shí)的幀率異常問題記錄
- 2022-07-12 ERROR:ORA-12543: TNS:destination host unreachable
- 2022-10-07 numpy中數(shù)組拼接、數(shù)組合并方法總結(jié)(append(),?concatenate,?hstack,
- 2022-04-21 IDEA - Spring Boot 項(xiàng)目 application.yml 文件不加載的問題
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支