就像大多數(shù)現(xiàn)在的變成語言一樣,在 Swfit 中你就像生活在一個(gè)幸福的世界中帘皿,這里的內(nèi)存被額外的部分所管理靠柑,而像這樣的內(nèi)存管理語言的編譯和運(yùn)行要么就像 Swift 一樣,要么他運(yùn)行的好壞取決于他的垃圾回收機(jī)制之碗。而這些我們所提到的這些隱藏在編程語言中的,你不必要去或者很少的情況下你需要去思考這些問題季希。
然而由于 Swift 的多樣性的特點(diǎn)褪那,你可能需要調(diào)用一個(gè)危險(xiǎn)的 C 的 Api 比如說 OpenGL 或者 POSIX 中的函數(shù),在這些情況下你可能需要處理一些讓我們頭疼的情況式塌。沒錯(cuò)博敬,我說的就是指針和手動(dòng)在堆中申請內(nèi)存空間。
在 Swift 3.0 以前峰尝,Swift 的不安全的 API 有點(diǎn)混亂偏窝,你可以通過好幾個(gè)方法來達(dá)到相同的結(jié)果,但是那只是你從 stackoverflow 上復(fù)制武学、粘貼來的祭往,但是你沒有徹底的理解真正發(fā)生了什么。在 Swift 3.0 中所有事物都發(fā)生了改變劳淆,而且他變得更容易理解链沼。
在這篇文章中,我不會(huì)告訴你如何將代碼從 Swift 2.x 遷移到 Swift 3.0沛鸵。反而我將會(huì)告訴你這些事情在 Swift 3.0 中如何工作括勺,因?yàn)橥ǔT斐刹话踩囊玫闹饕蚴桥c C 的底層 API 的交互缆八。
讓我們從最簡單的操作開始——開辟內(nèi)存空間來存儲(chǔ)一個(gè)整型變量。
在 C 中疾捍,你將會(huì)寫下下面這樣的代碼
而這在 Swift 在這么實(shí)現(xiàn)的:
第一類我們所看到的是 Swift 中的UnsafeMutablePointer,這普通的結(jié)構(gòu)體相當(dāng)于一個(gè) T 的指針奈辰,正如你所示的,他有一個(gè)靜態(tài)函數(shù)乱豆,allocate將會(huì)開辟需要的內(nèi)存空間奖恰。
正如你所想的,這個(gè)UnsafeMutablePointer還有一個(gè)變形——UnsafePointer宛裕,這個(gè)類型不允許你修改指針的值瑟啃,此外不可修改的UnsafePointer甚至沒有allocate方法。
在 Swift 中揩尸,你還有另外一個(gè)方法來創(chuàng)建一個(gè)Unsafe[Mutable]Pointer方法蛹屿,那就是使用&操作。當(dāng)傳一個(gè) block 或者函數(shù)岩榆,你可以使用一個(gè)&來傳入一個(gè)指針错负。讓我們來看下面這個(gè)例子
&操作需要一個(gè)var變量,但是這個(gè)將會(huì)提供給你你所需要解決的各種情況勇边。比如說犹撒,你可以使用可修改的引用(mutable reference),甚至修改它粒褒,比如說:
&操作需要一個(gè)var變量识颊,但是這個(gè)將會(huì)提供給你你所需要解決的各種情況。比如說奕坟,你可以使用可修改的引用(mutable reference)谊囚,甚至修改它,比如說:
這個(gè)例子和前面那個(gè)例子有重要的區(qū)別执赡。在前面的例子中,我們需要手動(dòng)開辟內(nèi)存空間(我們需要在創(chuàng)建好后手動(dòng)分配內(nèi)存空間)函筋,同時(shí)在這個(gè)簡單的例子中的函數(shù)中沙合,我們快速的創(chuàng)建了一個(gè)指向內(nèi)存的指針。明顯的跌帐,管理內(nèi)存并且使用指針指向他是2個(gè)不同的話題首懈,在接下來的例子中,我們將會(huì)聊一聊如何管理內(nèi)存空間谨敛。
但是我們?nèi)绾卧赟wift中如何在不創(chuàng)建一個(gè)函數(shù)的情況下究履,調(diào)用指針。為了達(dá)到這種目的脸狸,我們需要使用withUnsafeMutablePointer最仑,他將會(huì)調(diào)用一個(gè) Swift 的引用類型和一個(gè)有參數(shù)的 block 藐俺,讓我們來看看下面這個(gè)例子。
現(xiàn)在我們知道了這個(gè)方法泥彤,現(xiàn)在我們調(diào)用 C 中那些有指針的 API 欲芹,讓我們看來看下下面這個(gè)POSIX的打開讀取路徑并獲取其中內(nèi)容的當(dāng)前地址的方法。
指針轉(zhuǎn)換
當(dāng)處理 C 的 API 的時(shí)候吟吝,你有時(shí)候需要將指向結(jié)構(gòu)體的指針轉(zhuǎn)換為不同的結(jié)構(gòu)體菱父。對于 C 的 API 的處理很簡單(同時(shí)也是十分危險(xiǎn)并且容易出現(xiàn)報(bào)錯(cuò))的,就像你在 Swift 中所看到的剑逃,所有指針的類型是被固定的浙宜,這意味著一個(gè)UnsafePointer的指針不能再用在需要UnsafePointer的地方,這使得能夠更好的編寫出更加安全的代碼蛹磺,但是同樣意味著你不能在你需要的時(shí)候隨意轉(zhuǎn)換指針類型粟瞬。比如說 socket 中的bind()方法比如說這些情況下,我們將會(huì)使用withMemoryRebound這個(gè)我們用來將一個(gè)指針類型轉(zhuǎn)換為另一個(gè)指針類型的方法称开。讓我們來看看我們是如何使用角色轉(zhuǎn)換亩钟,在bind函數(shù)中當(dāng)你創(chuàng)建一個(gè)sockaddr_in結(jié)構(gòu)體轉(zhuǎn)換為sockaddr
這個(gè)一個(gè)用來轉(zhuǎn)變指針類型的特別的方法,一些 C 的 API 需要傳一個(gè)void*指針鳖轰。在 Swift 3.0 以前清酥,你可能需要使用UnsafePointer。然而在3.0中有一個(gè)新的類型來處理這些指針:UnsafeRawPointer蕴侣。這個(gè)結(jié)構(gòu)體和不同的結(jié)構(gòu)體不同焰轻,所以這意味著他不會(huì)將其中的信息綁定到任何指定的類型中,這另我們的編碼過程變得很簡單昆雀。為了創(chuàng)建一個(gè)UnsafeRawPointer指針辱志,我們只需要調(diào)用它的創(chuàng)建函數(shù)來包裹我們所需要的那個(gè)指針。如果我們想要用另外的方法狞膘,來將這個(gè) UsafeRawPointer 的指針轉(zhuǎn)化為其他類型的指針的時(shí)候揩懒,我們需要使用withMemoryRebound的上一個(gè)版本的方法,在這里他叫做assumingMemoryBound挽封。
數(shù)組指針
到這里已球,我們我們已經(jīng)學(xué)會(huì)了一些指針的基本使用方法,同時(shí)你可以處理大多數(shù)的 C 的 API 調(diào)用辅愿。然而指針使用的地方還有很多智亮,比如說遍歷內(nèi)存塊,這對于程序員來說是我們可以獲得很多重要信息点待。在 Swift 中我們有一些方法來做這些事情阔蛉,比如說 UnsafePointer 有提供了一個(gè)方法advanced(by:)來遍歷內(nèi)存,這個(gè)方法返回了另一個(gè) UnsafePointer 癞埠,這樣我們就可以讀寫那個(gè)內(nèi)存區(qū)域里面的內(nèi)容状原。
另外聋呢, Swift 還有一個(gè)UnsafeBufferPointer的結(jié)構(gòu)體來更方便的實(shí)現(xiàn)這個(gè)需求。這個(gè)結(jié)構(gòu)體是一個(gè)Swift數(shù)組和指針的橋梁遭笋。如果我們使用UnsafePointer來作為變量從而調(diào)用創(chuàng)建函數(shù)創(chuàng)建一個(gè)UnsafeBufferPointer坝冕,我們將能夠使用大多數(shù)的Swift原生的數(shù)組(Array)方法,因?yàn)閁nsafeBufferPointer遵守并實(shí)現(xiàn)了Collections瓦呼,Indexable和RandomAccessCollection協(xié)議喂窟。所以我們可以像這樣遍歷內(nèi)存:
當(dāng)我們提到UnsafeBufferPointer的是一個(gè)Swift中數(shù)組的橋梁的時(shí)候,這也意味著我們很容易使用UnsafeBufferPointer來調(diào)用一個(gè)已經(jīng)存在的數(shù)組央串,比如說下面這個(gè)例子:
內(nèi)存管理帶來的危害
我們已經(jīng)看到了很多方法來引用原始內(nèi)存磨澡,但是我們不能忘記我們正在進(jìn)入一個(gè)危險(xiǎn)區(qū)域≈屎停可能重復(fù)Unsafe單詞可能會(huì)提醒我們要小心的使用它們稳摄。然而我們我們是使用unsafe引用來混合兩個(gè)世界(不需要內(nèi)存管理和手動(dòng)內(nèi)存管理)。讓我們來看看他在我們靈活使用中所帶來的危害饲宿。
雖然這個(gè)簡單的例子我們不會(huì)真正的碰到厦酬,但是實(shí)際在快速創(chuàng)建變量的過程中我們會(huì)碰到和他類似但是比他更加復(fù)雜的代碼。在這里瘫想,collection在一個(gè) block 中被創(chuàng)建仗阅,同時(shí)在 block 結(jié)束后引用被釋放。我們有意的在調(diào)用collection后將引用保存在了collectoinPtr中国夜,然后在原始的collection不在存在后繼續(xù)調(diào)用减噪,所以程序在調(diào)用duplicateElements(inArray:)后崩潰了,如果我們想要使用指針來快速創(chuàng)建變量车吹,我們需要確定這些變量能夠在我們需要使用它們的時(shí)候可用筹裕。注意ARC將在每個(gè)變量離開他的作用于的時(shí)候?yàn)槊總€(gè)變量添加release方法,如果這個(gè)變量沒有被強(qiáng)引用的話窄驹,他就會(huì)被釋放朝卒。
一個(gè)解決方法是不適用 Swift 的內(nèi)存管理方法而是我們自己手動(dòng)開辟內(nèi)存空間,就像我們文章中所提到的那些簡單的代碼一樣乐埠,這就解決了訪問無效引用的問題扎运,但是這引入了另一個(gè)問題。如果我們沒有手動(dòng)釋放內(nèi)存饮戳,那么就會(huì)存在內(nèi)存泄漏問題。
使用 bitPattern 來修改指針的值
為了更好地完成這篇文章洞拨,在這我將介紹一些 Swift 中指針的用法扯罐。 第一個(gè)就是在使用C的API的時(shí)候使用void*方法而不是使用內(nèi)存地址。通常這會(huì)發(fā)生在一個(gè)函數(shù)接受不同類型的參數(shù)烦衣,并簡單的將參數(shù)打包成 void* 類型歹河,就像下面這個(gè)例子一樣:
如果我們想要在 Swift 中調(diào)用第一個(gè)函數(shù)掩浙,我們需要使用特別的構(gòu)造函數(shù),這會(huì)創(chuàng)建一個(gè)特殊的地址的秸歧。所有這些函數(shù)將不會(huì)改變允許你改變內(nèi)存地址中變量的值厨姚,所以我們將會(huì)在這種情況下使用UnsafePointer(bitPattern:)。
透明指針
在這篇文章的最后我想說的就是如何使用 Swift 中的透明指針键菱。在C的 API 中我們經(jīng)常會(huì)調(diào)用用戶數(shù)據(jù)谬墙,而用戶的數(shù)據(jù)將會(huì)成為一個(gè)void*指針,該用戶數(shù)據(jù)將是一個(gè) void * 指針经备,他將保存在一個(gè)任意內(nèi)存中拭抬。一個(gè)通用的使用方法是當(dāng)處理函數(shù)并設(shè)置回調(diào)方法的時(shí)候,事件將會(huì)被調(diào)用侵蒙。在這種情況下造虎,傳入一個(gè)引用到一個(gè) Swift 對象中,然后我們就可以在 C 的回調(diào)函數(shù)中調(diào)用指針的方法纷闺。
我們能夠使用UnsafeRawPointer就像我們曾在這篇文章中的其他例子中所看到的算凿。然而正如我們所看到的,這些調(diào)用在內(nèi)存管理中有一定的問題犁功,當(dāng)我們傳入一個(gè)指針到 C 中來指向一個(gè)我們沒有 retain 的變量的時(shí)候氓轰,這個(gè)對象將被釋放,同時(shí)這個(gè)程序?qū)?huì)崩潰波桩。
Swift 有一個(gè)實(shí)用的方法來根據(jù)我們是否真的需要戒努,從而決定指向這個(gè)對象的指針是否進(jìn)行 retain 。這就是Unmanaged結(jié)構(gòu)體的一個(gè)靜態(tài)函數(shù)镐躲。使用passRetained()我們將會(huì)創(chuàng)建一個(gè)被 retained 的指向這個(gè)對象的指針储玫,那么我們就能保證當(dāng)他在 C 中被調(diào)用的時(shí)候他仍舊在那。當(dāng)這個(gè)對象已經(jīng)在回調(diào)函數(shù)中被 retianed 的時(shí)候我們可以使用passUnretained()萤皂。這兩個(gè)方法將會(huì)產(chǎn)生Unmanaged的實(shí)例變量撒穷,這個(gè)實(shí)力變量將會(huì)通過調(diào)用toOpaque()方法轉(zhuǎn)換為UnsafeRawPointer
在另一方面我們可以將UnsafeRawPointer通過相反的 APIfromOpaque()和takeRetained()轉(zhuǎn)換為一個(gè)類或者結(jié)構(gòu)體
上圖為2017年最新的視頻教程資料,搜索2352149755加我好友私聊我上傳視頻教程裆熙,有什么不懂的也可以來私聊問我端礼。
不定時(shí)更新中。
如果你能明白這些視頻資料的好差入录,那么你也算是入行了蛤奥,底層和中高層就是這一步之差。