iOS swift中值類型與引用類型的不同 – iPhone手機開發 iPhone軟體開發教學課程

iOS swift中值類型與引用類型的不同。

這裡寫圖片描述
這些內容是每一種編程語言的基礎。我們大部分人從C語言開始編程,如果你還記得通過值調用與引用調用的函數,你也許就清楚它們的區別到底是什麼。讓我們看看蘋果是怎麼回答的吧

就像標題所說,swift中一個類型可以歸入到下面兩個分類中

值類型 引用類型

最基本的定義:

值類型-每個實例都擁有其數據的一份副本。當被賦值給一個變量或常量,或傳遞給一個函數時候,它會建立一份新的副本。

讓我們看一些編碼

考慮下面的代碼
引用類型

引用類型

上面的類Home並沒有進行任何初始化。存儲的特性roomCount有一個默認值2。現在,看看第一個叫peterVilla的實例,他會有一個值為2的roomCount。

現在建立一個新的叫johnVilla的對象,並把這個對象按照上面的代碼賦值。你覺得johnVilla的roomCount值會是多少呢?他會和peterVilla的roomCount值一樣嗎?是的,它是2

現在把johnVilla的roomCount值變為5,之後打出它們的roomCount值,兩者都給出瞭5。
這裡寫圖片描述

原因:

類(class)是一個引用類型,復制一個引用,即表示建立一個共享的實例。復制之後,兩個變量都使用數據中同一份實例。所以修改第二個變量中的數據同樣會影響第一個。

註意:類是一個引用類型,意味著類中的變量不會存儲實例,而是一個向內存(堆)中存儲該實例位置的引用。

問題:如果我們把上面代碼中的var變成let會怎麼樣?

回答:什麼都不會變。像下面這樣運行的話:

let peterVilla = Home()
let johnVilla = peterVilla

對輸出結果不會有任何影響。roomCount還會是5,為什麼??

因為類是引用對象,let與var唯一的區別就是再賦值給同樣類型下不同類的能力。let與var並不會影響同一個類中改變一個變量的能力。

但是,看看下面的代碼
這裡寫圖片描述

上面的代碼就不言自明瞭。

簡單考慮:一旦我們創建一個HOME,然後給它一個let常數,我們就隻能改變roomCount。所以這裡因為它是不可變的(let),John無法升級他的房子。我們不能創建一個新的HOME或改變它,let會對我們生氣的。你現在應該明白瞭!

如果johnVilla是一個var呢?

如果如果johnVilla是一個var,那麼它是可變的,他就可以改變他的房屋,無論多少次都可以。看看下面的代碼吧:
這裡寫圖片描述

如果Home是一個結構(struct)呢?

考慮下面的代碼,其中Home是一個struct
這裡寫圖片描述

這裡Home是一個結構,johnVilla成為一個let常數,我們無法改變roomCount,就像上部分我們在類中一樣。

這是因為結構是一個值類型,在結構中用let會讓這個對象變為常數。它將不可改變或再賦值。一個用var創建的結構可以改變它的變量。

所以對johnVilla再賦值也會失敗。

let peterVilla = Home()
let johnVilla = peterVilla
johnVilla = Home()
//error: cannot assign to value: ‘johnVilla’ is a ‘let’ constant

註意:所以,對於值類型,如果我們想對對象再賦值或改變對象中的變量,它需要被聲明為可變的(‘var’)。

這裡寫圖片描述

上面的代碼很簡單,它涵蓋瞭各方面比如再賦值或者改變成員變量。盡管我們在第44行把peterVilla賦值給johnVilla,johnVilla是一個獨立的實例,擁有獨自的peterVilla的數據副本。

這就是說,struct並不是swift中唯一的值類型,class也不是唯一的引用類型。下面是一些例子:
這裡寫圖片描述
Swift把一個引用類型看成一個類,這和Objective-C中很像。Objective-C中一切繼承於NSObject都被按照引用類型存儲。

我們什麼時候選擇值類型而不用引用類型?

如果你想建立一個新類型,你要怎麼決定選哪一種呢?當你用Cocoa的時候,很多APIs期望NSObject的子類,所以你不得不采用類。對於其他情況,這裡有一些參考:

以下時候使用值類型:

想要用==比較實例數據。一個雙等號(==)用於比較值。 你想復制來建立獨立數據。 數據要在多線程的代碼中使用,那麼你就不用擔心數據會被其他線程改變。

以下時候使用引用類型(比如一個類):

想要用===比較實例一致性。===會檢查兩個對象是否完全一致,包括存儲數據的內存地址。 你想要創建用於共享,可改變的數據。

引用類型和值類型在內存中怎麼存儲?

值類型-在棧內存中存儲

引用類型-在托管堆內存中存儲

棧與堆的不同!

像前面說的,引用類型實例存在堆中,值類型實例比如結構存在於一個稱為棧的內存區域中。如果值類型實例是一個類的一部分,值會和類一起存在堆中。

棧被用於靜態存儲分配,棧用於動態存儲分配,它們都存在計算機的RAM中。

棧被CPU緊密管理並優化,當一個函數創建一個變量,棧會存儲這個變量,並在函數退出時候被毀掉。被分配到棧的變量直接存儲在內存上,訪問這段內存非常快。當一個函數或者方法調用另一個函數,另一個函數再依次調用其他函數等等,直到最後一個函數返回它的值之前,其他所有函數都會保持暫停執行。

棧總是按照LIFO順序保留,最新保留的區塊總是會下一個釋放。這使得跟蹤記錄棧非常簡單,釋放一個棧上的區塊不過是調整一個指針。因為棧非常組織有序,所以它快捷高效。

系統使用堆存儲被其他對象引用的數據,堆是一大片內存,系統可以從中請求並動態分配內存區塊。堆並不會像棧一樣自動毀掉它的對象,需要外部工作來處理這些。在蘋果設備中ARC就做這個工作。引用數量會被ARC追蹤,當它變為0時對象會被釋放。因此整個過程(分配,追蹤引用,釋放)會比棧要慢。所以值類型要快於引用類型。

發佈留言