1. 程式人生 > >Swift什麼時候使用結構體和類

Swift什麼時候使用結構體和類

答案其實很簡單:當你需要值語義(值語義是指一個物件被系統標準的複製方式複製之後,與被複制的物件之間毫無關係,彼此獨立改變且互不影響)的時候使用結構體,當你需要引用語義(引用語義是指一個物件被系統標準的複製方式複製後,與被複制的物件之間依然共享底層資源,對其中一個的改變都將影響到另外一個)的時候使用類。就是這樣!

歡迎下週再來。。。

等等!

怎麼了?

這沒有回答這個問題

什麼意思?就是這樣的啊!

是的,但是。。。

但是什麼?

什麼是值語義和引用語義呢?

哦,這個啊。也許我接下來應該講講這個。

並且他們怎麼和結構體和類相關聯的呢?

好的。

所有的問題都歸結於資料和資料被儲存在什麼地方。我們通常將資料存在在區域性變數、引數、屬性以及全域性變數中。從根本上說有兩種不同的方法將資料儲存在所有這些地方。

值語義中,資料直接存在於被儲存的位置。引用語義中,資料存在於別的地方,而儲存的位置中儲存著一個對資料的引用。當你獲取資料的時候這種差別可能不那麼明顯。而當你拷貝那塊儲存區域時這種不同就會顯現出來。值語義中,你會獲的原資料的一個新拷貝,而引用語義下,你會獲的同樣資料的引用的一個新拷貝。

這真的很抽象。讓我們來看一個例子,暫時把 Swift 的這個問題從你腦海中移除,讓我們來看一個 Objective-C 的例子:

@interface SomeClass : NSObject @property int number;@end@implementation SomeClass@endstruct SomeStruct { int
number;};SomeClass *reference = [[SomeClass alloc] init];reference.number = 42;SomeClass *reference2 = reference;reference.number = 43;NSLog(@"The number in reference2 is %d", reference2.number);struct SomeStruct value = {};value.number = 42;struct SomeStruct value2 = value;value.number = 43;NSLog(@"The number in value2 is %d"
, value2.number);\

列印結果:

The number in reference2 is 43The number in value2 is 42

為什麼會有這樣的差異呢?

程式碼SomeClass *reference = [[SomeClass alloc] init]在記憶體中建立了一個新的SomeClass型別的例項,然後將這個例項物件的引用賦值給變數。程式碼reference2 = reference是將剛剛這個例項物件的引用賦值給一個新的變數。現在兩個變數都指向了同一個物件,而reference.number = 43修改儲存在那個例項物件的number屬性的值。所以當列印物件的number屬性值時,結果是43

程式碼struct SomeStruct value = {}建立了SomeStruct的一個例項並賦值給變數。程式碼value2 = value是拷貝那個例項物件的資料給第二個變數。每個變數都包含了一塊獨立的資料。程式碼value.number = 43只修改了value變數中的資料,所以當列印value2number時結果仍然是42

這個例子對應的 Swift 程式碼如下:

class SomeClass { var number: Int = 0}struct SomeStruct { var number: Int = 0}var reference = SomeClass()reference.number = 42var reference2 = referencereference.number = 43print("The number in reference2 is \(reference2.number)")var value = SomeStruct()value.number = 42var value2 = valuevalue.number = 43print("The number in value2 is \(value2.number)")

和之前的列印結果一樣:

The number in reference2 is 43The number in value2 is 42

值型別的體驗

值型別其實並不是一個新的概念,但是對於很多人來說他們覺得這是新概念。為什麼呢?

大多數 Objective-C 程式碼中結構體並不是很常用。我們通常通過CGRect或者CGPoint以及其他類似結構的形式接觸他們,但是一般不會建立我們自己的結構體。其中的一個原因是他們其實並不是那麼實用。想要用 Objective-C 語言將一個物件的引用正確地儲存在一個結構體中真的是一件很困難的事情,尤其是在使用 ARC 的情況下。

很多其他的語言根本沒有類似struct這樣的型別。很多認為“一切皆物件”的語言如 Python、JavaScript 等也都只有引用型別。如果你是從那樣的語言轉而學習 Swift 的,這個概念對你來說可能會更陌生。

但是別急!有一種情況是幾乎所有的語言都使用值型別:數字!下面的例子就連剛開始學習程式設計幾周的程式設計師都不會覺得陌生,我們先拋開語言:

var x = 42var x2 = xx++print("x=\(x) x2=\(x2)")// prints: x=43 x2=42

這對我們來說是那麼的明顯和自然以至於我們根本沒有覺察到他表現得有些不同,但是它就那樣展現在我們面前。只要你在程式設計你就在跟值型別打交道,雖然有可能你沒有意識到!

很多語言實際上把數字作為引用型別來實現,因為他們堅持“一切皆物件”的哲學。不管怎樣,他們是不可變型別,而值型別和不可變引用型別之間的區別很難察覺。他們表現得跟值型別很像,即使他們不是像值型別那樣實現的。

這是關於理解值型別和引用型別的相當大的一部分內容。就語言的語義來說,只有在資料被改變的時候他們的差異才會有影響。但是如果你的資料是不可變的,那麼值型別和引用型別的差別就不存在了,至少問題就轉向效能而不是語法了。

這甚至出現在了 Objective-C 中的標記指標(tagged pointers)中。就像標記指標中那樣,一個物件儲存在一個指標的值中,這是個值型別。拷貝儲存區域就拷貝了物件。這個差別不明顯,因為 Objective-C 庫很小心地只在不可變型別中加入了標記指標。一些NSNumbers物件是引用型別,另外一些則是值型別,但是這並沒有什麼差別。

做出選擇

現在我們知道值型別是怎麼工作的了,你怎麼選擇你自己的資料型別呢?

從根本上講這兩者的區別就是當你在他們身上使用等號的時候發生了什麼。值型別被拷貝,而引用型別只是獲得另外一個引用。

因此當決定使用哪種資料型別時根本上要問的問題就是:拷貝這個型別有意義嗎?你想方便地使用拷貝操作並且會頻繁使用嗎?

先讓我們來看一些比較極端的,明顯的例子。Integer明顯是可以被拷貝的,他們應該是值型別的。網路套接字明顯不能被拷貝,他們應該是引用型別。point中的x,y是可以拷貝的,他們應該是值型別。一個代表著磁碟的控制器明顯不能被拷貝,他們應該是引用型別。

有些型別可以被拷貝但是你不想拷貝一直髮生。這就表明他們應該是引用型別的。例如,螢幕中的一個按鈕在概念上是應該能被拷貝的。但是拷貝的按鈕跟原始的那個並不完全一樣。你點選拷貝的按鈕並不會觸發原始的那個。拷貝的按鈕也不會佔據原始按鈕在螢幕中的位置。那就意味著你的button應該是引用型別的。

Viewwindow controllers就是一個類似的例子。他們可能是可拷貝的,這是極為有可能的,但是你幾乎永遠都不想那麼做,所以他們應該是引用型別的。

模型類會怎麼樣呢?你可能有一個User型別來代表你係統的使用者,或者一個Crime型別代表一個User的動作。這肯定是可拷貝的,所以他們應該是值型別的。然而,你可能想將程式中某個地方使用者的操作更新到到程式的另外一個地方使其可見。這意味著你的使用者應該被某個引用型別的user controller進行管理。

集合是個有趣的例子。他們包含了陣列,字典還有字串。他們是可拷貝的嗎?很明顯是。你想讓拷貝成為一項便捷又頻繁的操作嗎?那就不是很明確了。

大多數語言對這個問題說“no”並把他們的集合設定為引用型別。這在 Objective-C、Java、Python、JavaScript 以及任何我能想到的語言中都是這樣的(一個主要的例外就是 C++ 中的 STL 集合型別,但是 C++ 是語言界的奇葩,它總是表現得跟大家不一樣)。

Swift 對此說“yes”,那也就意味著Array,DictionaryString都是結構體而不是類。當他們被賦值以及作為引數被傳遞的時候都會被拷貝。如果拷貝的代價很小的話這絕對是明智的決定,而這也正是 Swift 很努力要做到的。

巢狀型別

當巢狀值和引用型別的時候有四種不同的組合。只這其中的一種就會讓你的生活變的很有趣。

如果你有一個引用型別嵌套了另外一個引用型別,沒有什麼特別的事會發生。像通常那樣,任何一個指向內部或者外部值的指標都能操縱他指向的物件。只要其中一個引用操縱值使其改變,其他引用指向的值也就跟著變了。

如果你有一個值型別嵌套了另外一個值型別,這就會有效地使值所佔的記憶體區域變大。內部值是外部值的一部分。如果你把外部值放到一塊新的儲存空間裡,所有的值包括內部值都會被拷貝。如果你把內部值放進一塊新的儲存空間中,只有內部值會被拷貝。

一個引用型別嵌套了一個值型別會有效擴大這個引用型別所佔記憶體區域。任何指向外部值的指標都可以操縱一切,包括巢狀的內部值。內部值的任何改變對於引用外部值的指標來說都是可見的。如果你把內部值放進一塊新的儲存區,就會在那塊儲存區拷貝一份新的值。

一個值型別巢狀一個引用型別就沒有那麼簡單了。你可以有效地打破值語義而不被察覺。這可能是好的也可能是壞的,取決於你怎麼做。當你把一個引用型別巢狀進一個值型別中,外部值被放進一塊新的記憶體區域時就會被拷貝,但是拷貝的物件仍然指向原始的那個巢狀物件。下面是一個舉例:

class Inner { var value = 42}struct Outer { var value = 42 var inner = Inner()}var outer = Outer()var outer2 = outerouter.value = 43outer.inner.value = 43print("outer2.value=\(outer2.value) outer2.inner.value=\(outer2.inner.value)”)

列印結果如下:

outer2.value=42 outer2.inner.value=43

儘管outer2獲取了value的一份拷貝,它只拷貝了inner的引用,因此兩個結構體就共用了同一個inner物件。這樣一來當改變outer.inner.value的值也會影響outer2.inner.value的值。哎呀!

這個行為會很有用。當你小心使用,你建立的結構體就具有寫時拷貝功能(只有當你執行outer2.value = 43時才會真正的產生一個副本,否則outer2outer仍指向共同的資源),這種高效的值語義的實現不會使資料拷貝得到處都是。Swift 中的集合就是這麼做的,你也可以自己建立一個這樣的型別。想要了解更多請看讓我們構建Swift陣列.

這也可能會很危險。例如,我們正在建立一個Person物件。這是一個模型類所以明顯是可拷貝的,所以它可以是結構體。按照通常的做法,你把Person類的name設定為NSString型別

struct Person { var name: NSString}

然後你建立兩個Person物件,並且分不同的部分建立名字:

let name = NSMutableString()name.appendString("Bob")name.appendString(" ")name.appendString("Josephsonson")

相關推薦

iOS(OC、swift結構的區別

OC: 相同點:都可以將多個數據封裝為一個整體。 不同點: 1. 結構體只能封裝資料,而類還可以封裝行為。 2. 賦值 :結構體是(拷貝),物件之間是(地址) 3. 結構體變數分配在棧空

Swift什麼時候使用結構

答案其實很簡單:當你需要值語義(值語義是指一個物件被系統標準的複製方式複製之後,與被複制的物件之間毫無關係,彼此獨立改變且互不影響)的時候使用結構體,當你需要引用語義(引用語義是指一個物件被系統標準的複製方式複製後,與被複制的物件之間依然共享底層資源,對其中一個的改變都將影響

結構的區別,聯系

變量賦值 屬性 left 運行 效率 整體 如果 場景 區別 結構體和類的共同點:都可以將多個數據封裝為一個整體結構體和類的不同點: 結構體只能封裝數據,而類還可以封裝行為; 結構體實例是值類型,類實例是對象類型 結構體實例存儲在棧空間,類實例存儲在堆空間 結構體變量賦值

結構的唯一區別就是函數沒有加說明是私有而結構函數是公有

com ref www 沒有 a20 www. itl post tro 結構體和類的唯一區別就是 沒有加說明 類函數 是 私有 而結構體函數是公有結構體和類的唯一區別就是類函數沒有加說明是私有而結構體函數是公有

C# 結構的區別

字段 long 姓名 table 衡量 int 結構體 需要 操作 第一個問題:,“結構體”和“類”有啥區別?怎樣辨別是結構體還是類!結構體是一種值類型,而類是引用類型。(值類型、引用類型是根據數據存儲的角度來分的)就是值類型用於存儲數據的值,引用類型用於存儲對實際數據的引

C#基礎:結構的區別

    結構體和類非常相似,結構體用struct修飾,類用class修飾,結構體是值型別,類是引用型別。 具體示例程式碼如下:  public class MyClass//類         {             public int val;      

C++進階--結構

// 單純從語言上來說,兩者唯一的區別是,預設成員是公有還是私有 // 從使用習慣上 // 小的消極物件,包含公有資料,沒有或僅有很少的基本的成員函式 -- 資料容器 struct Person_t { string name; unsigned age; }; // 大的積極物件,包含

結構區別

樓上說的很詳細 結構和類有什麼區別呢. 結構和類一樣 都同等於一個自定義類 但是結構是值型別 如果你在結構中有一個int a 你寫上一句int B = a的話 他們的值會是一樣 但是不管你怎樣改變其中的一個 另外一個也不受影響 因為這是值型別(基本資料型別) B只是a的值的副本 如果是類的

(1.1.12)結構的區別

 (1)在C++中只有兩點區別:   (1)class中預設的成員訪問許可權是private的,而struct中則是public的。         (2)從class繼承預設是private繼承,

結構的區別 (objective c)

 1. 結構體只能封裝屬性,而類不僅可以封裝屬性還可以封裝方法.       如果1個封裝資料既有屬性也有行為,只能用類.      2. 結構體變數分配在棧.OC物件分配在堆.     棧的空間相對較小.但是儲存在棧中的資料訪問效率相對較高.     堆的空間相對較大.但

ios-結構的區別

1、結構體只能封裝屬性,類卻不僅可以封裝屬性也可以封裝方法。如果一個封裝的資料有屬性也有行為,就只能用類了。 2、結構體變數分配在棧,而OC物件分配在堆,棧的空間相對於堆來說是比較小的,但是儲存在棧中的資料訪問效率相對於堆而言是比較高 3、堆的儲存空間比較大,儲存在堆中的資

C#中結構區別聯絡

結構體 結構體定義 結構體是一種值型別,通常用來封裝小型相關變數組。例如座標或者商品的特徵。 結構體是一種自定義的資料型別,相當於一個複合容器,可以儲存多種型別。 結構體由結構體成員構成,結構體成員包含欄位,屬性與方法 結構體建

C#中結構的區別

結構體和類同樣能夠定義欄位,方法和建構函式,都能例項化物件,這樣看來結構體和類的功能好像是一樣的了,但是他們在資料的儲存上是不一樣的C#結構體和類的區別問題:這兩種資料型別的本質區別主要是各自指向的記憶體位置不同。傳遞類的時候,主要表現為是否同時改變了源物件。1.結構體是值型

[C#]結構的區別

 結構體和類的區別:     在做一個專案時,使用了較多的結構體,並且存在一些結構體的巢狀,即某結構體成員集合包含另一個結構體等,總是出現一些奇怪的錯誤,才終於下決心好好分析一下到底類和結構體有啥不同,雖然它們很相似,但確實有很大的不同,用不好難免出的問題會比較多,現總

【知識積累】C#中結構的區別

【類】     類是對現實生活中一類具有共同特徵的事物的抽象。類的實質是一種資料型別,類似於int、char等基本型別,不同的是它是一種複雜的資料型別。因為它的本質是型別,而不是資料,所以不存

淺析C#中的結構 筆記

類和結構是 .NET Framework 中的常規型別系統的兩種基本構造。 兩者在本質上都屬於資料結構。封裝著一組總體作為一個邏輯單位的資料和行為。 資料和行為是該類或結構的“成員”,它們包括各自的方法、屬性和事件等 對於C/C++程式設計師來說。結構體和類的差別非常小。僅僅是結構體的預設成員變

淺析C#中的結構

類和結構是 .NET Framework 中的常規型別系統的兩種基本構造。 兩者在本質上都屬於資料結構,封裝著一組整體作為一個邏輯單位的資料和行為。 資料和行為是該類或結構的“成員”,它們包含各自的方法、屬性和事件等 對於C/C++程式猿來說,結構體和類的區別

c++ 結構的區別

區別: 結構是一種用關鍵字struct宣告的自定義資料型別。與類相似,也可以包含建構函式,常數,欄位,方法,屬性,索引器,運算子和巢狀型別等,不過,結構是值型別。 1.結構的建構函式和類的建構函式不同。    a.結構不能包含顯式的無引數建構函式。結構成員將自動初始化為

go 結構 函式方法 介面

go中沒有類的概念,只有結構體。 定義結構體使用type struct 關鍵字 type typeName struct { var1 Type var2 Type ... varn Type } 其它面向物件的語言中類包含屬性和方法,go

c#字典中傳入結構在賦值時候的區別

//我以為這裡可以和c++中的map一樣,可以直接賦值,但是會報錯 //這裡不能這樣直接賦值 //然後我是用方法1中那種方式處理的,但我感覺這麼處理很麻煩 //然後我在csdn論壇上問了下,然後我換成方法2,這種方法可以直接賦值,不再那麼繁瑣 //可以說是各有各的好處吧