Swift基礎語法(十七)Swift的指標實現

語言: CN / TW / HK

持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第14天,點選檢視活動詳情

Swift基礎語法文章彙總

本文主要介紹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 帶泛型

程式碼:

帶泛型指標.webp

說明: 1. 通過指標的pointee來拿到記憶體資料 2. 依然是取地址符拿到指標,只不過需要儲存到Swift提供的指標型別中 3. 注意Mutable的可以進行修改

2.2 無泛型

程式碼:

無泛型指標.webp

說明: 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 拿到變數的指標

程式碼:

變數指標.webp

說明: * 這裡很明顯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. 因為傳入的引數不一定是正確的,所以是可失敗的初始化器

方式二:通過變數獲取變數內容作為指標

物件指標.webp

說明: 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.allocate(capacity: 3) ptr.initialize(to: 11) ptr.successor().initialize(to: 22) ptr.successor().successor().initialize(to: 33)

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:

ssumingMemoryBound方式轉換型別.webp

說明: 1. 非泛型指標轉泛型指標可以通過這個方法來轉

第三種:unsafeBitCast: 轉換時會忽略資料型別的強制轉換,不會因為資料型別的變化而改變原來的記憶體資料

unsafeBitCast型別轉換.webp

說明: 1. 正常的資料型別轉換,會改變儲存的資料 2. 通過unsafeBitCast的轉換不會改變記憶體資料