關於Swift中的指針的那些事

前言

Objective-c的世界中,一切對象都是指針。它是一種運行時語言,具體指針的對象類型將會在運行時,由系統分配。這樣雖然自由,可是卻並不安全。編程

Swift世界就不同了,Swift的世界很安全(至少大部分時候狀況如此)。咱們沒必要爲對象運行時的類型擔心,這是Swift爲咱們構築的一層堡壘。可是在一些時候,這層堡壘也成爲束縛咱們行爲的操做。swift

正文

Swift也爲操做指針這種不安全行爲提供了支持。數組

MemoryLayout

MemoryLayout是Swift 爲結構體struct等一些須要獲取具體內存空間大小定義的枚舉。安全

它包含一系列方法,可使咱們更方便的使用。bash

主要屬性就是三個:socket

/// 對應類型的實例在內存佔用的真實字節大小
public static var size: Int { get }
/// 對應類型的實例在內存通過對齊後佔用的字節大小 (內存對齊請參照百度百科,連接將會在下文給出)
public static var stride: Int { get }
/// 默認內存對齊單位
public static var alignment: Int { get }
複製代碼

內存對齊百度百科: baike.baidu.com/item/內存對齊/9…編程語言

這三個屬性也是咱們常用的,對應還有三個從實例對象中獲取的方法ide

playgroud 介紹代碼函數

struct Test {
    let res1: Bool = false
    let res2: Int = 0
    let res3: Bool = false
}

print("\(MemoryLayout<Test>.stride)") // 24
複製代碼

由於這個類不是咱們首要要介紹的,因此就只是大概介紹學習

內存指針

指針定義:

在計算機科學中,**指針(Pointer)**是編程語言中的一個對象,利用地址,它的值直接指向(points to)存在電腦存儲器中另外一個地方的值。因爲經過地址能找到所需的變量單元,能夠說,地址指向該變量單元。所以,將地址形象化的稱爲「指針」。意思是經過它能找到以它爲地址的內存單元。

節選自百度百科: baike.baidu.com/item/指針/287…

Swift中認爲全部的指針相關操做都是不安全的,因此全部指針相關的結構體都會包含有前綴Unsafe

Swift 中一共有8個結構體

Swift Objective-c 描述
UnsafePointer<T> const T * 指針及其所指向的內存內容均不可變
UnsafeMutablePointer<T> T * 指針及其所指向的內存內容都可變
UnsafeBufferPointer<T> const T * [] 是一個指針數組內容均不可變
UnsafeMutableBufferPointer<T> T * []
UnsafeRawPointer const void *
UnsafeMutableRawPointer void *
UnsafeBufferRawPointer void * []

首先要說一個實例類對象如何轉化爲指針

UnsafePointer代碼展現:

struct Obj {
    var name: Int = 5
}

var obj = Obj()

let pointer = withUnsafePointer(to: &obj, {$0})

print("\(pointer.pointee)")
複製代碼

這裏注意了,UnsafePointer.pointee只有get方法,也就是至關於咱們獲取了一個let的對象,聽從於let的相關要求。

注:這裏多介紹一下letvar的區別

letvar 都是swift聲明變量時候的前綴。在程序中,當咱們聲明變量的時候,在真實運行時候的環境裏,程序便會爲咱們聲明的變量初始化對應的內存。

let聲明的變量的內存內容,在聲明初始化事後便再也不可變

var聲明的變量的內存內容,在隨後能夠隨時被修改。

這裏咱們使用的是內存內容,也就是抽象的內存地址裏的那些0、1的值。

對於classletvar的區別是這個變量能不能再被賦值一個新的類對象,而對原有對象的相關存儲屬性的修改,並不受影響。這是由於class的對象在聲明時候的堆棧內存內容,只有8個字節(咱們可使用MemoryLayout獲取),真實對象的存儲在其餘位置。因此咱們能修改let指向的值

對於structletvar的區別就是let建立的對象,不論屬性是用let仍是var,咱們都沒法直接修改值。只能修改var修飾的。這個是由於struct對象是直接存儲在聲明時候的內存內容裏。

對於UnsafeMutablePointer來講,pointee即是setget方法了,也就是說,咱們能夠直接修改對應內存地址的值了。

這裏展現下如何獲取UnsafeMutablePointer與修改相關值

struct Obj {
    var name: Int = 5

    init(name: Int = 5) {
        self.name = name
    }
}

var obj = Obj()

let pointer = withUnsafeMutablePointer(to: &obj, {$0})

print("Obj: \(obj)")

pointer.pointee = Obj(name: 10)

print("\(pointer.pointee)")

print("Obj: \(obj)")
複製代碼

當運行這段代碼的時候,咱們能夠看到,obj的內容也隨着指針變化了。

注:obj只能使用var聲明,由於Unsafe類的調用須要使用&,同時有可能產生內存內容的變化,所以let定義下不符合相關要求。

接下來是我要講的就是Swift中等同於C語言中void *的指針UnsafeRawPointer

有可能到這裏就會有疑問了,咱們已經有了UnsafePointer了,這個UnsafeRawPointer究竟有什麼用呢?

在蘋果的官方文檔中,關於UnsafeRawPointer的定義爲

A raw pointer for accessing untyped data.

一個用來訪問未定義類型的指針

Overview

The UnsafeRawPointer type provides no automated memory management, no type safety, and no alignment guarantees. You are responsible for handling the life cycle of any memory you work with through unsafe pointers, to avoid leaks or undefined behavior.

UnsafeRawPointer 提供了沒有自動內存管理,沒有類型安全,與內存對齊控制保證。你有義務去本身管理你經過指針引用的任何內存的聲明週期,以免內存泄漏或者其他未定義(不安全)的行爲

Memory that you manually manage can be either untyped or bound to a specific type. You use the UnsafeRawPointer type to access and manage raw bytes in memory, whether or not that memory has been bound to a specific type.

你手動管理的內存能夠是未定義內存的,或者是被定義到一個指定的類型的。你可使用UnsafeRawPointer去訪問和管理未被定義的內存字節,而無需在乎這段內存是否已經被綁定到一個指定的類型。

官方定義是這樣的,大體思路就是,若是你有需求須要本身管理內存的時候,使用這個類。通常就是咱們操做底層的一些代碼,這個會須要被當成參數傳入。

代碼示例:

var i = 2

let pointerRaw = withUnsafeBytes(of: &i, {$0})

guard let pointer = pointerRaw.bindMemory(to: Int.self).baseAddress else { fatalError("這段代碼理論上不能執行到這") }

let j = pointer.pointee

print("j:\(j)")
複製代碼

以上代碼就是轉換rawPointer和pointer的例子了。剩下的是bufferPointer,由於使用的地方較少了,故此不深刻介紹了。

總結

指針相關中最重要的幾個操做就是指針的生成,與將指針轉化成實例對象。其中重要的就是pointee和幾個withUnsafeXX的全局函數。

swift中,指針的世界並不複雜,全部指針的操做都能轉化爲8個UnsafeXX類。咱們的使用也是基於此的,咱們若是使用某些底層方法須要指針,這個時候只須要將相應的指針對象生成便可傳入。

結尾

一轉眼已經元旦了,期間也想過寫一些文章,可是對於沒什麼東西要寫感到困惑。同時,這段時間也是一直在學習Swift的相關語言。由於我的項目中將要用到socket操做,因而決定用Swift重寫GCDAsyncSocket。在重寫過程當中遇到了不少指針相似的問題,因而決定寫一篇關於指針的教程。

Swift的socket庫SwiftAsyncSocket目前正在完善中,TCP/IP協議基礎功能已基本完善。近期將會正式開源

本文首發於,本人博客與公衆號(見下圖),若是但願轉載到公衆號,請聯繫本人開通權限。

公衆號

最後,祝你們新年快樂,心想事成

相關文章
相關標籤/搜索