類和結構體是通用的,靈活的結構,成為程序代碼的基礎。 您可以通過使用與常量,變量和函數完全相同的語法來定義屬性和方法來向類和結構添加功能。
與其他編程語言不同,Swift不需要為自定義類和結構創建單獨的接口和實現文件。 在Swift中,您可以在單個文件中定義一個類或結構,并且該類或結構的外部接口自動可供其他代碼使用。
類的實例傳統上稱為對象。 但是,Swift類和結構在功能上比其他語言更接近,本章的很多內容描述了可應用于類或結構類型實例的功能。 因此,使用更一般的術語實例。
類和結構體的比較
Swift中的類和結構有很多共同點。 兩者都可以:
- 定義要存儲值的屬性
- 定義方法以提供功能
- 定義下標,以使用下標語法訪問其值
- 定義初始化程序以設置其初始狀態
- 擴展其功能超出默認實現
- 符合協議以提供某種類型的標準功能
有關更多信息,請參閱屬性,方法,下標,初始化,擴展和協議。
類具有結構不具有的附加功能:
繼承使一個類能夠繼承另一個類的特性。
- 類型轉換使您能夠在運行時檢查和解釋類實例的類型。
- Deinitializers使一個類的實例釋放它分配的任何資源。
- 引用計數允許對類實例的多個引用。
有關更多信息,請參閱繼承,類型轉換,取消初始化和自動引用計數。
語法定義
類和結構體具有類似的語法定義,引入一個類的class關鍵字和一個結構體的struct關鍵字,兩者都將整個定義放在一個花括號內
class SomeClass {
// class definition goes here
}
struct SomeStructure {
// structure definition goes here
}
這里有一個結構定義和類定義的例子:
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}
類和結構體的實例
Resolution結構定義和VideoMode類定義僅描述Resolution或VideoMode的外觀。 他們自己沒有描述特定的Resolution或VideoMode。 為此,您需要創建結構或類的實例。
對于結構和類,創建實例的語法非常相似:
let someResolution = Resolution()
let someVideoMode = VideoMode()
結構和類都對新實例使用初始化語法。 初始化語法的最簡單形式使用類或結構的類型名稱,后跟空括號,例如Resolution()或VideoMode()。 這將創建一個類或結構的新實例,并將任何屬性初始化為其默認值。 類和結構初始化在初始化中有更詳細的描述。
訪問屬性
您可以使用點語法訪問實例的屬性。 在點語法中,您在實例名稱后立即寫入屬性名稱,以句點(.)分隔,不帶任何空格:
print("The width of someResolution is \(someResolution.width)")
// Prints "The width of someResolution is 0"
翻譯關閉即時翻譯
在本例中,someResolution.width指的是someResolution的width屬性,并返回其默認初始值0。
您可以深入查看子屬性,例如VideoMode的resolution屬性中的width屬性:
print("The width of someVideoMode is \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is 0"
您還可以使用點語法為變量屬性分配新值:
someVideoMode.resolution.width = 1280
print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is now 1280"
注意:
與Objective-C不同,Swift允許您直接設置結構屬性的子屬性。 在上面的最后一個例子中,someVideoMode的resolution屬性的width屬性是直接設置的,而不需要將整個resolution屬性設置為一個新值。
結構體類型的成員初始化器
所有結構都具有自動生成的成員初始化器,您可以使用它來初始化新結構實例的成員屬性。 新實例的屬性的初始值可以按名稱傳遞到成員初始化程序:
let vga = Resolution(width: 640, height: 480)
與結構不同,類實例不接收默認的成員初始化。 在初始化中更詳細地描述初始化器。
結構和枚舉是值類型
值類型是一種類型,當它被賦值給一個變量或常量,或者當它被傳遞給一個函數時,它的值被復制。
事實上,Swift整數,浮點數,布爾值,字符串,數組和字典中的所有基本類型都是值類型,并且作為幕后結構實現。
所有結構和枚舉都是Swift中的值類型。 這意味著,您創建的任何結構和枚舉實例(以及它們作為屬性的任何值類型)在您的代碼中傳遞時總是被復制。
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.width = 2048
print("cinema is now \(cinema.width) pixels wide")
// Prints "cinema is now 2048 pixels wide"
print("hd is still \(hd.width) pixels wide")
// Prints "hd is still 1920 pixels wide"
當給予cinema當前值hd時,存儲在hd中的值被復制到新的cinema實例中。 最終結果是兩個完全獨立的實例,剛剛發生包含相同的數值。 因為它們是單獨的實例,將cinema設置為2048不會影響存儲在hd中的寬度。
同樣的行為適用于枚舉:
enum CompassPoint {
case north, south, east, west
}
var currentDirection = CompassPoint.west
let rememberedDirection = currentDirection
currentDirection = .east
if rememberedDirection == .west {
print("The remembered direction is still .west")
}
// Prints "The remembered direction is still .west"
當rememberedDirection被賦值為currentDirection的值時,它實際上被設置為該值的副本。 之后更改currentDirection的值不會影響存儲在rememberedDirection中的原始值的副本。
類是引用類型
與值類型不同,引用類型在分配給變量或常量時或者傳遞給函數時不會被復制。 也不是副本,而是使用對同一現有實例的引用。
這里有一個例子,使用上面定義的VideoMode類:
let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0
let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0
print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// Prints "The frameRate property of tenEighty is now 30.0"
注意,tenEighty和TenEighty被聲明為常量,而不是變量。 但是,您仍然可以更改tenEighty.frameRate和TenEighty.frameRate,因為tenEighty和AlsoTenEighty常量本身的值實際上不會更改。 tenEighty和TenEighty自己不會“存儲”VideoMode實例,而是它們都指向幕后的VideoMode實例。 它是底層VideoMode的frameRate屬性更改,而不是該VideoMode的常量引用的值。
身份操作符
因為類是引用類型,所以有可能多個常量和變量在后臺引用一個類的同一個單一實例。 (對于結構和枚舉不是這樣,因為它們在分配給常量或變量或傳遞給函數時總是被復制。)
有時可能有用的是找出兩個常量或變量是否指向一個類的完全相同的實例。 為了實現這一點,Swift提供了兩個身份操作符:
- 相同(===)
- 不同于(!==)
使用這些運算符來檢查兩個常量或變量是否引用同一個單個實例:
if tenEighty === alsoTenEighty {
print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// Prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."
"=="和"==="的意義是不相同的
- “與之相同”意味著類類型的兩個常量或變量引用完全相同的類實例。
- “等于”是指兩個實例在值上被認為是“等于”或“等效”,對于類型的設計者定義的一些適當的“等于”含義。
當您定義自己的自定義類和結構時,您有責任決定什么是“等于”的兩個實例。 在等價運算符中描述了定義自己的“等于”和“不等于”運算符的實現的過程。
指針
如果你有C,C ++或Objective-C的經驗,你可能知道這些語言使用指針來引用內存中的地址。 引用某個引用類型的實例的Swift常量或變量類似于C中的指針,但不是直接指向內存中地址的指針,并且不要求您寫入星號(*)以指示您 正在創建一個引用。 相反,這些引用的定義與Swift中的任何其他常量或變量一樣。
在類和結構之間選擇
作為一般準則,考慮在以下一個或多個條件適用時創建結構體:
- 該結構的主要目的是封裝一些相對簡單的數據值。
- 當您分配或傳遞該結構的實例時,期望封裝的值將被復制而不是被引用是合理的。
- 由結構存儲的任何屬性都是值類型,這也將被期望被復制而不是被引用。
- 該結構不需要從另一個現有類型繼承屬性或行為。
字符串,數組和字典的賦值和復制行為
在Swift中,許多基本數據類型,例如String,Array和Dictionary被實現為結構體。 這意味著,如果字符串,數組和字典等數據被分配給一個新的常量或變量,或者當它們傳遞給一個函數或方法時,它們就被復制。
這個行為不同于Foundation:NSString,NSArray和NSDictionary實現為類而不是結構。 Foundation中的字符串,數組和字典總是分配和傳遞作為對現有實例的引用,而不是作為副本。