成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Swift 中的指針使用

移動開發 iOS
Apple 期望在 Swift 中指針能夠盡量減少登場幾率,因此在 Swift 中指針被映射為了一個泛型類型,并且還比較抽象。這在一定程度上造成了在 Swift 中指針使用的困難,特別是對那些并不熟悉指針,也沒有多少指針操作經驗的開發者 (包括我自己也是) 來說,在 Swift 中使用指針確實是一個挑戰。在這篇文章里,我希望能從最基本的使用開始,總結一下在 Swift 中使用指針的一些常見方式和場景。這篇文章假定你至少知道指針是什么。

Apple 期望在 Swift 中指針能夠盡量減少登場幾率,因此在 Swift 中指針被映射為了一個泛型類型,并且還比較抽象。這在一定程度上造成了在 Swift 中指針使用的困難,特別是對那些并不熟悉指針,也沒有多少指針操作經驗的開發者 (包括我自己也是) 來說,在 Swift 中使用指針確實是一個挑戰。在這篇文章里,我希望能從最基本的使用開始,總結一下在 Swift 中使用指針的一些常見方式和場景。這篇文章假定你至少知道指針是什么,如果對指針本身的概念不太清楚的話,可以先看看這篇五分鐘 C 指針教程 (或者它的中文版本),應該會很有幫助。

初步

在 Swift 中,指針都使用一個特殊的類型來表示,那就是 UnsafePointer<T>。遵循了 Cocoa 的一貫不可變原則,UnsafePointer<T> 也是不可變的。當然對應地,它還有一個可變變體,UnsafeMutablePointer<T>。絕大部分時間里,C 中的指針都會被以這兩種類型引入到 Swift 中:C 中 const 修飾的指針對應 UnsafePointer (最常見的應該就是 C 字符串的 const char * 了),而其他可變的指針則對應 UnsafeMutablePointer。除此之外,Swift 中存在表示一組連續數據指針的 UnsafeBufferPointer<T>,表示非完整結構的不透明指針 COpaquePointer 等等。另外你可能已經注意到了,能夠確定指向內容的指針類型都是泛型的 struct,我們可以通過這個泛型來對指針指向的類型進行約束以提供一定安全性。

對于一個 UnsafePointer<T> 類型,我們可以通過 memory 屬性對其進行取值,如果這個指針是可變的 UnsafeMutablePointer<T> 類型,我們還可以通過 memory 對它進行賦值。比如我們想要寫一個利用指針直接操作內存的計數器的話,可以這么做:

  1. func incrementor(ptr: UnsafeMutablePointer<Int>) { 
  2.     ptr.memory += 1 
  3.  
  4. var a = 10 
  5. incrementor(&a) 
  6.  
  7. a  // 11 

這里和 C 的指針使用類似,我們通過在變量名前面加上 & 符號就可以將指向這個變量的指針傳遞到接受指針作為參數的方法中去。在上面的 incrementor 中我們通過直接操作 memory 屬性改變了指針指向的內容。

與這種做法類似的是使用 Swift 的 inout 關鍵字。我們在將變量傳入 inout 參數的函數時,同樣也使用 & 符號表示地址。不過區別是在函數體內部我們不需要處理指針類型,而是可以對參數直接進行操作。

  1. func incrementor1(inout num: Int) { 
  2.     num += 1 
  3.  
  4. var b = 10 
  5. incrementor1(&b) 
  6.  
  7. b  // 11 

雖然 & 在參數傳遞時表示的意義和 C 中一樣,是某個“變量的地址”,但是在 Swift 中我們沒有辦法直接通過這個符號獲取一個 UnsafePointer 的實例。需要注意這一點和 C 有所不同:

  1. // 無法編譯 
  2. let a = 100 
  3. let b = &a 

指針初始化和內存管理

在 Swift 中不能直接取到現有對象的地址,我們還是可以創建新的 UnsafeMutablePointer 對象。與 Swift 中其他對象的自動內存管理不同,對于指針的管理,是需要我們手動進行內存的申請和釋放的。一個 UnsafeMutablePointer 的內存有三種可能狀態:

  • 內存沒有被分配,這意味著這是一個 null 指針,或者是之前已經釋放過
  • 內存進行了分配,但是值還沒有被初始化
  • 內存進行了分配,并且值已經被初始化

其中只有第三種狀態下的指針是可以保證正常使用的。UnsafeMutablePointer 的初始化方法 (init) 完成的都是從其他類型轉換到 UnsafeMutablePointer 的工作。我們如果想要新建一個指針,需要做的是使用 alloc: 這個類方法。該方法接受一個 num: Int 作為參數,將向系統申請 num 個數的對應泛型類型的內存。下面的代碼申請了一個 Int 大小的內存,并返回指向這塊內存的指針:

  1. var intPtr = UnsafeMutablePointer<Int>.alloc(1) 
  2. // "UnsafeMutablePointer(0x7FD3A8E00060)" 

接下來應該做的是對這個指針的內容進行初始化,我們可以使用 initialize: 方法來完成初始化:

  1. intPtr.initialize(10) 
  2. // intPtr.memory 為 10 

在完成初始化后,我們就可以通過 memory 來操作指針指向的內存值了。

在使用之后,我們***盡快釋放指針指向的內容和指針本身。與 initialize: 配對使用的 destroy 用來銷毀指針指向的對象,而與 alloc: 對應的 dealloc: 用來釋放之前申請的內存。它們都應該被配對使用:

  1. intPtr.destroy() 
  2. intPtr.dealloc(1) 
  3. intPtr = nil 

注意其實在這里對于 Int 這樣的在 C 中映射為 int 的 “平凡值” 來說,destroy 并不是必要的,因為這些值被分配在常量段上。但是對于像類的對象或者結構體實例來說,如果不保證初始化和摧毀配對的話,是會出現內存泄露的。所以沒有特殊考慮的話,不論內存中到底是什么,保證 initialize: 和 destroy 配對會是一個好習慣。

指向數組的指針

在 Swift 中將一個數組作為參數傳遞到 C API 時,Swift 已經幫助我們完成了轉換,這在 Apple 的官方博客中有個很好的例子:

  1. import Accelerate 
  2.  
  3. let a: [Float] = [1, 2, 3, 4] 
  4. let b: [Float] = [0.5, 0.25, 0.125, 0.0625] 
  5. var result: [Float] = [0, 0, 0, 0] 
  6.  
  7. vDSP_vadd(a, 1, b, 1, &result, 1, 4) 
  8.  
  9. // result now contains [1.5, 2.25, 3.125, 4.0625] 

對于一般的接受 const 數組的 C API,其要求的類型為 UnsafePointer,而非 const 的數組則對應 UnsafeMutablePointer。使用時,對于 const 的參數,我們直接將 Swift 數組傳入 (上例中的 a 和 b);而對于可變的數組,在前面加上 & 后傳入即可 (上例中的 result)。

對于傳參,Swift 進行了簡化,使用起來非常方便。但是如果我們想要使用指針來像之前用 memory 的方式直接操作數組的話,就需要借助一個特殊的類型:UnsafeMutableBufferPointer。Buffer Pointer 是一段連續的內存的指針,通常用來表達像是數組或者字典這樣的集合類型。

  1. var array = [1, 2, 3, 4, 5] 
  2. var arrayPtr = UnsafeMutableBufferPointer<Int>(start: &array, count: array.count) 
  3. // baseAddress 是***個元素的指針 
  4. var basePtr = arrayPtr.baseAddress as UnsafeMutablePointer<Int> 
  5.  
  6. basePtr.memory // 1 
  7. basePtr.memory = 10 
  8. basePtr.memory // 10 
  9.  
  10. //下一個元素 
  11. var nextPtr = basePtr.successor() 
  12. nextPtr.memory // 2 

指針操作和轉換

withUnsafePointer

上面我們說過,在 Swift 中不能像 C 里那樣使用 & 符號直接獲取地址來進行操作。如果我們想對某個變量進行指針操作,我們可以借助 withUnsafePointer 這個輔助方法。這個方法接受兩個參數,***個是 inout 的任意類型,第二個是一個閉包。Swift 會將***個輸入轉換為指針,然后將這個轉換后的 Unsafe 的指針作為參數,去調用閉包。使用起來大概是這個樣子:

  1. var test = 10 
  2. test = withUnsafeMutablePointer(&test, { (ptr: UnsafeMutablePointer<Int>) -> Int in 
  3.     ptr.memory += 1 
  4.     return ptr.memory 
  5. }) 
  6.  
  7. test // 11 

這里其實我們做了和文章一開始的 incrementor 相同的事情,區別在于不需要通過方法的調用來將值轉換為指針。這么做的好處對于那些只會執行一次的指針操作來說是顯而易見的,可以將“我們就是想對這個指針做點事兒”這個意圖表達得更加清晰明確。

unsafeBitCast

unsafeBitCast 是非常危險的操作,它會將一個指針指向的內存強制按位轉換為目標的類型。因為這種轉換是在 Swift 的類型管理之外進行的,因此編譯器無法確保得到的類型是否確實正確,你必須明確地知道你在做什么。比如:

  1. let arr = NSArray(object: "meow"
  2. let str = unsafeBitCast(CFArrayGetValueAtIndex(arr, 0), CFString.self) 
  3. str // “meow” 

因為 NSArray 是可以存放任意 NSObject 對象的,當我們在使用 CFArrayGetValueAtIndex 從中取值的時候,得到的結果將是一個 UnsafePointer<Void>。由于我們很明白其中存放的是 String 對象,因此可以直接將其強制轉換為 CFString。

關于 unsafeBitCast 一種更常見的使用場景是不同類型的指針之間進行轉換。因為指針本身所占用的的大小是一定的,所以指針的類型進行轉換是不會出什么致命問題的。這在與一些 C API 協作時會很常見。比如有很多 C API 要求的輸入是 void *,對應到 Swift 中為 UnsafePointer<Void>。我們可以通過下面這樣的方式將任意指針轉換為 UnsafePointer。

  1. var count = 100 
  2. var voidPtr = withUnsafePointer(&count, { (a: UnsafePointer<Int>) -> UnsafePointer<Void> in 
  3.     return unsafeBitCast(a, UnsafePointer<Void>.self) 
  4. }) 
  5. // voidPtr 是 UnsafePointer<Void>。相當于 C 中的 void * 
  6.  
  7. // 轉換回 UnsafePointer<Int> 
  8. var intPtr = unsafeBitCast(voidPtr, UnsafePointer<Int>.self) 
  9. intPtr.memory //100 

總結

Swift 從設計上來說就是以安全作為重要原則的,雖然可能有些啰嗦,但是還是要重申在 Swift 中直接使用和操作指針應該作為***的手段,它們始終是無法確保安全的。從傳統的 C 代碼和與之無縫配合的 Objective-C 代碼遷移到 Swift 并不是一件小工程,我們的代碼庫肯定會時不時出現一些和 C 協作的地方。我們當然可以選擇使用 Swift 重寫部分陳舊代碼,但是對于像是安全或者性能至關重要的部分,我們可能除了繼續使用 C API 以外別無選擇。如果我們想要繼續使用那些 API 的話,了解一些基本的 Swift 指針操作和使用的知識會很有幫助。

對于新的代碼,盡量避免使用 Unsafe 開頭的類型,意味著可以避免很多不必要的麻煩。Swift 給開發者帶來的***好處是可以讓我們用更加先進的編程思想,進行更快和更專注的開發。只有在尊重這種思想的前提下,我們才能更好地享受這門新語言帶來的種種優勢。顯然,這種思想是不包括到處使用 UnsafePointer 的 :)

責任編輯:閆佳明 來源: onevcat.com
相關推薦

2015-03-16 10:33:14

Swift指針

2014-08-01 15:16:05

SwiftC語言

2015-10-13 10:00:58

Swift隨機數使用總結

2025-05-22 09:32:23

2011-04-11 11:09:50

this指針

2014-08-14 10:12:45

SwiftNil Coalesc

2015-07-08 16:43:02

Configurati

2015-11-23 10:07:19

Swift模式匹配

2022-11-04 09:01:33

SwiftPlottable

2021-12-22 15:13:03

iOS 15Swift二進制

2023-03-10 09:00:47

SwiftActors

2016-03-24 09:53:24

swiftguardios

2022-05-11 09:01:54

Swift類型系統幻象類型

2022-07-04 08:54:39

Swift處理器項目

2024-01-25 11:42:00

C++編程指針常量

2012-10-22 16:50:35

IBMdw

2011-04-19 09:19:09

C++指針

2023-10-26 11:19:21

指針Go

2011-04-19 16:38:00

對象指針指針C++

2022-01-19 09:00:00

Java空指針開發
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 福利精品 | 国产精品欧美一区二区三区 | 成人网av | 亚洲国产一区二区三区在线观看 | 97国产一区二区 | 日韩视频精品在线 | 五月天婷婷久久 | 日韩欧美一区二区三区在线播放 | 日韩一区二区三区视频在线观看 | 麻豆精品国产91久久久久久 | 久久久黑人 | 欧美九九九| 久久国产精品99久久久久久丝袜 | 亚洲精品日韩一区二区电影 | 日韩在线不卡 | 日本精品视频 | 日韩欧美在线视频一区 | 一级片在线观看 | 成人在线播放网站 | 91在线视频观看免费 | 日韩精品久久一区二区三区 | 美女久久 | 精品久久久久久亚洲综合网站 | 欧美亚洲视频在线观看 | 免费毛片网站在线观看 | 99精品一级欧美片免费播放 | av在线播放网站 | 国产精品99久久久久久久久 | 精品福利一区 | h视频亚洲| 国产精品一区二区三区久久 | 久久国产欧美日韩精品 | 午夜视频导航 | 久久久精品 | 日本高清视频在线播放 | 色毛片| 国产91成人 | 色婷婷久久 | 欧美在线视频网 | 精品久久久久久久久久久 | 国产福利在线播放 |