Swift基礎語法(十七)Swift的指標實現
持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第14天,點選檢視活動詳情
本文主要介紹Swift的指標型別,本質也是地址,但是Swift提供專門的指標型別儲存地址。
主要內容: 1. 指標型別的認識 2. 指標型別的使用 3. 型別轉換
1、指標認識
Swift中也有專門的指標型別,這些都被定性為“Unsafe”(不安全的),Swift中並不會簡單的認為地址就是一個指標,而是有專門的型別進行包裝。只要獲取到地址就可以對資料進行無訪問限制的操作,因此是不安全的。
指標型別:
| 指標型別 | 認識 | |:-----------------------------:|:----------------------:| | UnsafePointer< Pointee > | 類似於 const Pointee * | | UnsafeMutablePointer< Pointee > | 類似於 Pointee * | | UnsafeRawPointer | 類似於 const void * | | UnsafeMutableRawPointer | 類似於 void * |
說明: 1. 指標都是不安全的,因此都是Unsafe 2. 沒有Mutalbe的表示僅可讀指標指向的記憶體,帶有Mutable表示可讀可寫 3. < Pointee >是泛型,表示指標的型別 4. 帶Raw的都是不支援泛型的,所以都是void型別,型別不定
2、指標的使用
2.1 帶泛型
程式碼:
說明: 1. 通過指標的pointee來拿到記憶體資料 2. 依然是取地址符拿到指標,只不過需要儲存到Swift提供的指標型別中 3. 注意Mutable的可以進行修改
2.2 無泛型
程式碼:
說明: 1. 沒有設定型別,需要自己設定一下型別 2. 注意賦值為load和取值為storeBytes即可
2.3 通過指標遍歷陣列
程式碼:
var arr = NSArray(objects: 11, 22, 33, 44)
arr.enumerateObjects { (obj, idx, stop) in
print(idx, obj)
if idx == 2 { // 下標為2就停止遍歷
stop.pointee = true//指標賦值
}
}
說明:
1. 這種遍歷方式中stop引數其實就是一個指標型別()
2. 指標拿到自己的pointee就可以進行修改了。
3、獲取指標變數
3.1 獲取變數的指標
3.1.1 拿到帶泛型的指標
var age = 11
//帶泛型指標
var ptr1 = withUnsafeMutablePointer(to: &age) { $0 }
var ptr2 = withUnsafePointer(to: &age) { $0 }
ptr1.pointee = 22
print(ptr2.pointee) // 22
print(age) // 22
說明:
* with開頭的可以獲取,最後一個引數是閉包表示式
withUnsafeMutablePointer方法認識:
@inlinable public func withUnsafeMutablePointer<T, Result>(to value: inout T, _ body: (UnsafeMutablePointer<T>) throws -> Result) rethrows -> Result
說明:
1. 第一個引數傳的就是指標
2. 第二個引數是一個閉包表示式,獲取最終的Result
3. 閉包表示式的引數其實就是傳入的指標
4. 並且可以看到閉包表示式返回的東西就是這個withUnsafePointer返回的東西
3.1.2 拿到無泛型的指標
//無泛型指標
var ptr3 = withUnsafeMutablePointer(to: &age) { UnsafeMutableRawPointer($0) }
var ptr4 = withUnsafePointer(to: &age) { UnsafeRawPointer($0) }
ptr3.storeBytes(of: 33, as: Int.self)
print(ptr4.load(as: Int.self)) // 33
print(age) // 33
說明:
* 這裡是通過無泛型指標的初始化器來設定的,傳入的就是帶泛型的指標,所以可以直接傳入$0
3.1.3 拿到變數的指標
程式碼:
說明: * 這裡很明顯ptr拿到的是person變數的地址值。指標的本意嘛,不用多言。
3.1.4 拿到物件的指標
方式一:直接獲取物件地址作為指標 ``` class Person { var age: Int init (age: Int) { self.age = age } } var person = Person(age: 18) var ptr = withUnsafePointer(to: &person) { $0 } print("變數指標:",ptr)
var personPointer = UnsafeMutableRawPointer?(bitPattern: ptr) print("物件指標:",personPointer) ``` 說明: 1. 呼叫bitPattern初始化器,裡面傳入的是物件的地址,此時就可以將該地址包裝成指標,也就是放到一個全域性區/棧 2. 因為傳入的引數不一定是正確的,所以是可失敗的初始化器
方式二:通過變數獲取變數內容作為指標
說明: 1. 這裡傳入person變數,unsafeBitCast函式就可以拿到這個變數的內容賦值給ptr 2. 並且設定的型別就是UnsafeRawPointer。 3. 通過這種方式就可以很方便的得到物件的堆空間地址
3.2 建立一個無指向的指標
方式一:
//方式一:
var ptr = malloc(16)
ptr?.storeBytes(of: 10, as: Int.self)
ptr?.storeBytes(of: 20, toByteOffset: 8, as: Int.self)
free(ptr)//釋放記憶體
說明:
1. malloc建立一個空間,此時拿到的ptr的型別為:(注意肯定為可選項)
2. 按照正常的新增流程進行新增。注意總共16個位元組,一次性賦值只賦給了前8個位元組。所以還需要再賦後8個位元組,toByteOffset是偏移量
方式二:
//方式二:
var ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
ptr.storeBytes(of: 11, as: Int.self)
ptr.advanced(by: 8).storeBytes(of: 22, as: Int.self)
print(ptr.load(as: Int.self)) // 11
print(ptr.advanced(by: 8).load(as: Int.self)) // 22
ptr.deallocate()
說明:
1. 通過allocate進行建立,填入位元組數和對齊數
2. advanced(by: 8)是將ptr指標偏移8個位元組,並且返回一個指標。所以它得到的就是後8個位元組的指標
3. 最後需要通過deallcate銷燬
方式三:
```
//方式三:
var ptr = UnsafeMutablePointer
print(ptr.pointee) // 11 print((ptr + 1).pointee) // 22 print((ptr + 2).pointee) // 33
print(ptr[0]) // 11 print(ptr[1]) // 22 print(ptr[2]) // 33
ptr.deinitialize(count: 3) ptr.deallocate() ``` 說明: 1. 如果帶有泛型建立指標,那麼可以直接設定容量,這裡的容量是多少個值,而非位元組數 2. 在設定值時,可以直接用initialize(repeating: ,count:)重複設定兩個值,每個都是10 3. 也可以用initialize()只設置第一個值 4. ptr.successor()的作用就是偏移8個位元組拿到其指標 5. (ptr + 1)是指標偏移,直接偏移8個位元組 6. 這裡ptr+1、ptr[1]等價的 7. 最後需要銷燬記憶體
注意:無泛型指標會進行位元組偏移,而不是指標偏移:如果是泛型指標,因為已經知道佔用記憶體大小了,所以是可以進行指標偏移的,如果是非泛型指標,不能進行指標偏移,因為不知道一次性偏移多少
4、型別轉換
第一種:非泛型指標的初始化:
var ptr = withUnsafeMutablePointer(to: &age) { UnsafeMutableRawPointer($0) }
var ptr2 = withUnsafePointer(to: &age) { UnsafeRawPointer($0) }
說明:
1. 泛型指標轉非泛型指標可以通過非泛型指標的初始化器來設定
第二種:assumingMemoryBound:
說明: 1. 非泛型指標轉泛型指標可以通過這個方法來轉
第三種:unsafeBitCast: 轉換時會忽略資料型別的強制轉換,不會因為資料型別的變化而改變原來的記憶體資料
說明: 1. 正常的資料型別轉換,會改變儲存的資料 2. 通過unsafeBitCast的轉換不會改變記憶體資料
- WKWebView詳解(三)Cookie的認識
- Swift基礎語法(十七)Swift的指標實現
- SwiftUI教程(七)屬性包裝器:State、Binding、ObservableObject、EnvironmentObject
- SwiftUI教程(五)使用List建立列表應用程式
- SwiftUI教程(三)常用View和Modifiers詳細講解和使用
- Swift基礎語法(十一)泛型
- Swift基礎語法(六)閉包表示式和閉包
- Swift基礎語法(三)函式
- Swift基礎語法(一)常量變數、註釋、運算子、資料型別的認識
- 08 - OC多執行緒之認識和使用
- 14 - block的底層分析
- OC Runtime指導文件閱讀
- NSproxy虛基類實現代理和多繼承以及多型
- WKWebView詳解(二)- WebKit框架認識
- 21 - RunLoop的深入分析
- 16 - 小物件型別TaggedPointer
- 19 - 記憶體管理方案之自動釋放池AutoRelease
- 18 - 記憶體管理之retain/release/dealloc/retainCount的底層分析
- NSproxy虛基類實現代理和多繼承以及多型
- 回撥函式的認識