類和結構體是構建代碼所用的一種通用的構造體。都是可以使用完全相同的語法規則為類和結構體定義屬性(常量、變量)、添加方法,從而擴展類和結構體的功能。
注意: 通常一個
類
的實例稱之為對象
。但在swift中,類和結構體的關系要比在其他語言中更加的密切。為此,更多是使用實例
而不是對象。
類和結構體的共同之處:
- 定義屬性用于存儲值;
- 定義方法用于提供功能;
- 定義附屬腳本用于訪問值;
- 定義構造器用于生成初始化值;
- 通過擴展以增加默認實現的功能;
- 實現協議以提供某種標準功能;
(注: 后續章節會講到屬性
、方法
、下標腳本
、構造過程
、擴展
、協議
)
與結構體相比,類附加以下功能:
- 一個類允許繼承另外一個雷的特征;
- 類型轉換允許在運行時檢查和解釋一個類實例的類型;
- 析構器允許一個類實例釋放任何其所分配的資源;
- 引用計數允許對一個類的多次引用;
注意: 結構體總是通過復制的方式在代碼中傳遞,不適用引用計數。
一、類和結構體基本使用
- 定義語法。類和結構體定義方式類似,通過關鍵字
class
表示類,通過關鍵字struct
表示結構體,并在大括號中定義具體內容:
// 貓的類
class CatClass {
// 名字屬性,可選類型
var name:String?
// 顏色屬性
var color:UIColor?
}
// 屏幕的結構體
struct Screen {
// 屏幕寬度屬性
var w = 0
// 屏幕高度屬性
var h = 0
}
- 類和結構體的實例,類和結構體實例類似。結構體和類都使用構造器語法來生成新的實例,構造器語法的最簡單方式是在類或結構體類型名后面跟隨一對空括號即可,而這種方式創建的類或結構體實例,其屬性都會初始化為默認值:
// 類的實例
let xiaomiao = CatClass()
// 結構體的實例
var myScreen = Screen()
- 屬性訪問,使用點語法。規則是,實例名后面跟隨屬性名,兩者通過點
.
連接:
// 類實例
let xiaomiao = CatClass()
xiaomiao.name = "xiaomiao"
xiaomiao.color = UIColor.whiteColor()
// 結構體實例
var myScreen = Screen();
myScreen.w = 320
myScreen.h = 568
- 結構體類型的成員逐一構造器,所有結構體都有一個自動生成的成員逐一構造器,用于初始化結構體實例中的成員屬性:
var myScreen = Screen(w:320, h:400);
注: 與結構體不同,類實例沒有默認的成員逐一構造器。
二、結構體和枚舉是值類型
值類型被賦予給一個變量、常量或者被傳遞給一個函數時,其值會被拷貝。在swift中,所有的基本類型:整形、浮點數、布爾值、字符串、數組、字典,都是屬于值類型,并且在底層都是以結構體的形式實現。
?在swift中,所有的結構體和枚舉類型都是值類型,意味著它們的實例,以及實例中所包含的任何類型屬性,在代碼中傳遞的時候都會被復制。
// 屏幕結構體
struct Screen {
var w = 0
var h = 0
}
// 實例結構體
var myScreen1 = Screen(w:320, h:568);
// 賦值,結構體是值傳遞類型,即復制一份
var myScreen2 = myScreen1;
// 修改myScreen2,但myScreen1中是沒有任何影響的
myScreen2.h = 400;
// 實際myScreen1和myScreen2都是完全不同的實例
輸出結果:
print("myScreen1 --- w:\(myScreen1.w) h:\(myScreen1.h)");
print("myScreen2 --- w:\(myScreen2.w) h:\(myScreen2.h)");
三、類是引用類型
與值類型不同,引用類型在賦予到一個變量、常量或被傳遞到一個函數時,其值不會被拷貝。因為引用操作的都是已存在的實例本身。
// 定義貓類
class CatClass {
var name:String?
var color:UIColor?
}
// 貓咪多多
let duoduo = CatClass();
duoduo.name = "多多";
duoduo.color = UIColor.whiteColor();
// 賦值,cat其實表示就是duoduo這個對象本身
let cat = duoduo;
// 修改名字
cat.name = "貓星人-多多";
print("duoduo名字:\(duoduo.name)");
print("cat名字:\(cat.name)");
輸出結果:
duoduo名字:貓星人-多多
cat名字:貓星人-多多
類是引用類型,所以在上面例子中
duoduo
和cat
是引用同一個CatClass
實例,即是同一個實例,不同名字。
另外,聲明的時候使用let
,依舊可以改變cat.name
。因為duoduo
和cat
這兩個常量的值為改變,只是改變它們引用實例中的name
屬性而已。
- 等價于(
===
)和不等價于(!==
),當你要判斷兩個常量或者變量是否引用同一個類實例的時候進行使用:
// 判斷duoduo和cat是否引用同一個實例
if duo duo === cat {
print("引用的是同一個實例");
}
注意: 等價于(用三個等號表示
===
)與等于(用兩個等號表示==
)不同:
"等價于"表示兩個類型的常量或變量引用同一個類實例;
"等于"表示兩個實例的值"相等"或"相同";
四、類和結構體的選擇
類和結構體都可以用來自定義數據類型,但結構體實例總是通過值傳遞,類實例總是通過引用傳遞。
?考慮使用結構體:
- 該數據結構的主要目的是用來封裝少量相關簡單數據;
- 預計該數據結構的實例在賦值或傳遞時,封裝的數據將會被拷貝而不是被引用;
- 該數據結構中存儲的值類型,也應該被拷貝,而不是被引用;
- 該數據結構體不需要去繼承另一個既有類型的屬性或行為;
其他情況下,定義一個類,生成一個它的實例,并通過引用來管理和傳遞。實際中,意味著絕大部分的自定義數據構造都應該是內,而不是結構體。
五、字符串、數組、字典類型的賦值與復制行為
在swift中,許多基本類型,例如String、Array、Dictionary類型都是以結構體的形式實現。即意味著被賦值或作為參數傳遞時,它們的值會被拷貝。
?Objective-C中NSString、NSArray、NSDictionaty類型都是類的形式實現,而不是結構體。即意味著被賦值或作為參數傳遞時,不會發生值拷貝,而是傳遞現有實例的引用。
注意: 以上是字符串、數組、字典的"拷貝"行為的,即是拷貝行為看似總會發生。但swift中,指在絕對必要時才執行實際的拷貝。swift管理所有的值拷貝確保性能最優化,所以我們不必去回避賦值來確保性能最優化。