Swift 指針

因?yàn)槲也⒉皇怯?jì)算機(jī)專(zhuān)業(yè)出身,所以下面對(duì)C語(yǔ)言和指針的理解都來(lái)自于很久很久上過(guò)的C課程和工作以后接觸的oc以及swift。說(shuō)的不對(duì)的地方歡迎指正。

首先需要說(shuō)明的概念是比特(bit),理論上,這是一個(gè)計(jì)算系統(tǒng)最小的單位,不是0就是1,可以簡(jiǎn)單理解成為,通電和斷電。八個(gè)比特組成一個(gè)字節(jié)(byte),因?yàn)楝F(xiàn)代家用和商用的操作系統(tǒng)一般是32位或者64位系統(tǒng)。那么這是什么意思呢?不嚴(yán)謹(jǐn)?shù)睦斫饩褪遣僮飨到y(tǒng)指揮cpu一次性拾取這么多位數(shù)據(jù)。也就是說(shuō)32位系統(tǒng)一次從內(nèi)存讀取4個(gè)字節(jié)到32位cpu而64位系統(tǒng)一次讀取8個(gè)字節(jié)到cpu。讀到這里,作為一個(gè)工程師,你肯定會(huì)問(wèn)以下兩個(gè)問(wèn)題:

1. 那我要是一個(gè)數(shù)據(jù)占不滿(mǎn)這么多位怎么辦?。?2. 我要是數(shù)據(jù)超出了cpu怎么解決???

如果不是工程師,那么有可能會(huì)覺(jué)得問(wèn)這樣問(wèn)題的人是不是有病?沒(méi)滿(mǎn)就沒(méi)滿(mǎn)啊,滿(mǎn)了就再開(kāi)一個(gè)不就好了?。渴聦?shí)上,這涉及到一個(gè)最簡(jiǎn)單的工程思維,很多“常識(shí)性”的浪費(fèi)其實(shí)并不是真正浪費(fèi)。比如有一個(gè)叫做對(duì)齊的概念。懂一點(diǎn)計(jì)算機(jī)常識(shí)的人或多或少都聽(tīng)過(guò)這個(gè)概念,比如ssd的4k對(duì)齊。那么是內(nèi)存對(duì)齊呢?其實(shí)就是利用占位符(也就是0)來(lái)填滿(mǎn)沒(méi)有被使用的內(nèi)存空間,如果你要儲(chǔ)存一個(gè)int 10在內(nèi)存中,實(shí)際上是10 0 0 0 0 0 0 0 0,當(dāng)然,這里只是一個(gè)示例,顯然不可能是10存儲(chǔ)在內(nèi)存中。表面上看起來(lái)這是一種巨大的浪費(fèi),為什么我要占位而不能接著利用剩下來(lái)的空間呢?因?yàn)樵赾pu讀取的時(shí)候,對(duì)齊之后的數(shù)據(jù)可以大大降低讀寫(xiě)次數(shù),比如你要存儲(chǔ)“10個(gè)人”這三個(gè)字,如果我只想提取10出來(lái),那么我只需要找到指向integer的指針然后直接讀取所有步長(zhǎng)就可以了,這會(huì)很大程度上減少非必要的讀取次數(shù)。當(dāng)然除此以外,還有很多其他的原因比如原子性之類(lèi)的原因,具體的可以參考操作系統(tǒng)設(shè)計(jì)之類(lèi)的資料。那么當(dāng)我們說(shuō)讀取的時(shí)候是一個(gè)什么樣的概念呢?其實(shí)有點(diǎn)像送信,每個(gè)房屋都會(huì)在郵政有一個(gè)代碼,郵遞員就是根據(jù)這個(gè)代碼把郵件快遞送到你家的,每個(gè)位也有一個(gè)在操作系統(tǒng)注冊(cè)的代碼,操作系統(tǒng)就是依賴(lài)這個(gè)代碼來(lái)讓cpu去找到具體的數(shù)據(jù)的。

說(shuō)了這么多,所以到底什么是指針呢?指針就是一個(gè)整型數(shù)據(jù),儲(chǔ)存這個(gè)某一個(gè)數(shù)據(jù)在內(nèi)存的中的地址。一般人看到這里就會(huì)懵逼了,既然指針是指向數(shù)據(jù)的一個(gè)整數(shù),那么整數(shù)本身不也是一種數(shù)據(jù)嗎?你是不是在蒙我?這怎么聽(tīng)起來(lái)哪里不對(duì)?所以指針可以指向另外一個(gè)指針?答案就是這么粗暴,是這樣的。那你妹的不是太坑爹了嗎?所以你給了我一個(gè)整數(shù),我怎么知道這是一個(gè)數(shù)據(jù)還是一個(gè)指向數(shù)據(jù)的指針還是一個(gè)指向指針的指針?是的,你又猜對(duì)了,你不知道,至少對(duì)于swift來(lái)說(shuō),你不知道。很好,那怎么解決這個(gè)問(wèn)題呢?swift對(duì)c指針進(jìn)行了一些改造,給了你八種指針:

1. UnsafeMutablePointer<T>
2. UnsafePointer<T>
3. UnsafeMutableBufferPointer<T>
4. UnsafeBufferPointer<T>
5. UnsafeMutableRawPointer  
6. UnsafeMutableRawBufferPointer
7. UnsafeRawBufferPointer

又是一臉懵逼,這都什么玩意?c 語(yǔ)言里面就一個(gè)指針,swift什么鬼?給我這么多名字比我太奶奶裹腳布太長(zhǎng)的東西?冷靜一下,仔細(xì)看看這些東西,其實(shí)還是很有規(guī)律的,pointer當(dāng)然表示這些都是指針,unsafe表示不安全,use on your own risk。這句話(huà)的意思是,swift本身是一門(mén)安全的語(yǔ)言,你們可勁作,崩了算我輸。編譯器負(fù)責(zé)開(kāi)創(chuàng),管理,銷(xiāo)毀內(nèi)存中的數(shù)據(jù),所以像我這樣非計(jì)算機(jī)專(zhuān)業(yè)出身的智障開(kāi)發(fā)者,要用什么開(kāi)什么就好了,完全沒(méi)有任何技術(shù)含量,不需要任何計(jì)算機(jī)常識(shí)。但是,這個(gè)的前提是編譯器來(lái)管理內(nèi)存而不是開(kāi)發(fā)者,如果你就是這么任性,不聽(tīng)不聽(tīng),飛鞋點(diǎn)金。那么,swift能怎么辦?只能貼個(gè)免責(zé)申明,對(duì)吧,人之常情,你也經(jīng)??吹绞裁础皟H供研究學(xué)習(xí),不得用于商業(yè)”之類(lèi)的東西,unsafe就是這個(gè)意思。作為ios開(kāi)發(fā)者,mutable還是可以看得懂的,generic也是看得懂的,所以只剩下raw和buffer了。Raw表示這個(gè)指針直接指向數(shù)據(jù)而不是指向其他指針!?。語(yǔ)言常識(shí)告訴我們,所有變量名,函數(shù)名,struct名都是指針。所以這就是為什么指向這些東西的指針實(shí)質(zhì)上是指向指針的指針。Buffer是什么意思呢?其實(shí)就是這個(gè)單詞本身的意思,你可以理解為一個(gè)array,表示這個(gè)一個(gè)指針群,可能包含不止一個(gè)指針。這樣一來(lái),我們已基本能夠通過(guò)名字知道該如何使用一個(gè)指針了。

還記得C語(yǔ)言是如何創(chuàng)建一個(gè)整數(shù)的嗎? int x = 8. 這行代碼說(shuō)明了什么呢?說(shuō)明了int這個(gè)東西是一個(gè)raw value,所以呢,如果想要?jiǎng)?chuàng)建一個(gè)指向int的指針,我們應(yīng)該使用UnsafeMutableRawPointer:

let intPoint = UnsafeMutableRawPointer.allocate(bytes: bytes, alignedTo: alignment)

通過(guò)系統(tǒng)的Documentaion,我們可以找到上面這個(gè)方法來(lái)初始化一個(gè)指針,那么這個(gè)函數(shù)名很直白的告訴你,這個(gè)函數(shù)的作用是在heap區(qū)allocate一個(gè)空間,空間包含有bytes位數(shù)據(jù),按照alignment方法對(duì)齊。那么我們?cè)侔凑誨ocumentation可以很清楚的得知如何去開(kāi)創(chuàng)一個(gè)內(nèi)存空間:

/*
MemoryLayout<Int>
MemoryLayout是一個(gè)抽象類(lèi),使用的時(shí)候需要告訴這個(gè)類(lèi)具體的type
你可以使用你自己的class或者struct,MemoryLayout<MyClass>來(lái)獲取相關(guān)內(nèi)存的信息。
對(duì)于MemoryLayout,我們有三個(gè)很重要的屬性:size,alignment,stride。
當(dāng)你開(kāi)創(chuàng)了空間以后系統(tǒng)會(huì)自動(dòng)給這三個(gè)屬性賦值,會(huì)告訴你,內(nèi)存塊大小是多少,對(duì)齊方式是怎么樣的,遞進(jìn)步長(zhǎng)是多少。
如果我們要?jiǎng)?chuàng)建一個(gè)指向一個(gè)Int類(lèi)型數(shù)據(jù)的指針,那么首先,Int是不會(huì)超過(guò)一個(gè)byte的,對(duì)齊方式是64位對(duì)齊。
*/
let bytes = MemoryLayout<Int>.stride
let alignment = MemoryLayout<Int>.alignment
let intPoint = UnsafeMutableRawPointer.allocate(bytes: bytes, alignedTo: alignment)

如果你說(shuō),這他么有什么用?好,那你聽(tīng)過(guò)數(shù)組嗎?我們通過(guò)指針來(lái)創(chuàng)建一個(gè)指定大小swift數(shù)組Array<UInt>:

class IntArray {

    private let count:Int
    private var index = -1
    private var first = true
    private var start: UnsafeMutableRawPointer!
    private var bufferPointer: UnsafeRawBufferPointer!
    private let stride = MemoryLayout<Int>.stride
    private let alignment = MemoryLayout<Int>.alignment
    private let q = DispatchQueue(label: "ReservedQ")
    private var byteCount:Int {
        get{return stride * count}
    }
    
    init(count:Int) {
        self.count = count
        start = UnsafeMutableRawPointer.allocate(bytes: byteCount, alignedTo: alignment)
    }
    
    func append(_ newElement:UInt) {
        q.sync {
            guard index < count else {return}
            index += 1
            if first {
                first = false
                start.storeBytes(of: newElement, as: UInt.self)
            }else {
                start.advanced(by: stride * index).storeBytes(of: newElement, as: UInt.self)
            }
            bufferPointer = UnsafeRawBufferPointer(start: start, count: byteCount)
            for (i, byte) in bufferPointer.enumerated() {
                guard i < (index+1)*8 else {break}
                if byte != 0 && byte != 255 {
                    print(byte)
                }else if i%8 == 0 && byte == 0 {
                    print(0)
                }
            }
            print("--------------------")
        }
    }
    
    deinit {
        //這個(gè)函數(shù)是不可缺少的,這是unsafe的本質(zhì),you are on your own!你必須自己管理內(nèi)存了。
        start.deallocate(bytes: byteCount, alignedTo: alignment)
    }
}

運(yùn)行一下:

let arr = IntArray(count: 3)
arr.append(40)
arr.append(3)
arr.append(0)

結(jié)果是:
40
--------------------
40
3
--------------------
40
3
0
--------------------
當(dāng)然直接說(shuō)這個(gè)就是一個(gè)Array實(shí)在是太夸張了,但是,通過(guò)這么一個(gè)例子,我們可以更好的去理解如何在實(shí)際工作中去使用指針。畢竟對(duì)于iOS開(kāi)發(fā)來(lái)說(shuō),指針這種東西一般也就是用來(lái)寫(xiě)一寫(xiě)C函數(shù)的wrapper的。比如給keychain寫(xiě)一個(gè)wrapper,給一些第三方的SDK寫(xiě)一寫(xiě)Wrapper(可能在今天正常的公司都會(huì)給個(gè)最起碼是objective c的SDK,但是公司內(nèi)部的很多硬件SDK還是只有C和C++接口 T_T)。但是這并不表示指針這樣一樣強(qiáng)力工具對(duì)于iOS開(kāi)發(fā)就是不需要掌握的,因?yàn)槎羔樋梢宰屇阌煤芎?jiǎn)單的幾行代碼完成一些以前很難完成的工作。我們來(lái)看一個(gè)來(lái)自stackoverflow的例子:遍歷整個(gè)enum的所有case,如果你不會(huì)指針,那么你寫(xiě)出來(lái)的方案就會(huì)是:

enum ProductCategory : String {
     case Washers = "washers", Dryers = "dryers", Toasters = "toasters"

     static let allValues = [Washers, Dryers, Toasters]
     
}

for category in ProductCategory.allValues{
}

雖然看上去還可以,但是設(shè)想一下,如果你有十幾個(gè)case的話(huà),那么不僅很煩,而且還需要保證allValues array里面都是正確的。通過(guò)指針你就可以很輕松的使用一個(gè)static方法來(lái)完成:

enum ProductCategory : String {
    case Washers = "washers", Dryers = "dryers", Toasters = "toasters"
    
    static func iterateEnum() -> AnyIterator<ProductCategory> {
        var i = 0
        return AnyIterator {
            let next = withUnsafeBytes(of: &i) { $0.load(as: self) }
            if next.hashValue != i { return nil }
            i += 1
            return next
        }
    }
}

for category in ProductCategory.allValues{
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,052評(píng)論 4 61
  • 作者:Umberto Raimondi,原文鏈接,原文日期:2016-04-07譯者:shanks;校對(duì):pmst...
    梁杰_numbbbbb閱讀 5,556評(píng)論 0 20
  • 想必已經(jīng)使用Swift語(yǔ)言進(jìn)行開(kāi)發(fā)的小伙伴們都享受到了這門(mén)語(yǔ)言在開(kāi)發(fā)過(guò)程中帶來(lái)的便利,確實(shí)作為蘋(píng)果官方主推的編程語(yǔ)...
    文興閱讀 4,290評(píng)論 3 19
  • 前一段時(shí)間,管理了一個(gè)工程,趕了趕工期。 算起來(lái)是個(gè)文化旅游項(xiàng)目,走的是時(shí)下流行的“長(zhǎng)短縱橫學(xué)”?,F(xiàn)如今國(guó)內(nèi)的思想...
    德魯伊_Druid閱讀 947評(píng)論 0 0
  • 什么事情值得做,什么事情是有價(jià)值的? 在不同的人生階段,有著不同經(jīng)歷的人,會(huì)有不一樣的答案。但有一點(diǎn)我想你也應(yīng)該是...
    寧海的夏天閱讀 1,428評(píng)論 0 4

友情鏈接更多精彩內(nèi)容