結(jié)構(gòu)體和類是通用的橄唬、靈活的構(gòu)造,它們成為程序代碼的構(gòu)建塊参歹。使用與定義常量仰楚、變量和函數(shù)相同的語法,定義屬性和方法犬庇,以便向結(jié)構(gòu)和類添加功能僧界。
與其他編程語言不同,Swift不需要為自定義結(jié)構(gòu)和類創(chuàng)建單獨的接口和實現(xiàn)文件械筛。在Swift中捎泻,您在單個文件中定義一個結(jié)構(gòu)或類,該類或結(jié)構(gòu)的外部接口將自動提供給其他代碼使用埋哟。
注意:類的實例傳統(tǒng)上稱為對象笆豁。然而,Swift結(jié)構(gòu)和類在功能上比在其他語言中更接近赤赊,本章的大部分內(nèi)容描述了應(yīng)用于類或結(jié)構(gòu)類型實例的功能闯狱。因此,使用了更通用的術(shù)語instance抛计。
Comparing Structures and Classes 比較結(jié)構(gòu)體和類
相同性:
- 定義屬性來存儲值
- 定義方法來提供功能
- 定義下標以使用下標語法提供對其值的訪問
- 定義初始化器來設(shè)置它們的初始狀態(tài)
- 擴展以擴展其功能哄孤,使其超出默認實現(xiàn)
- 遵守協(xié)議以提供某種標準功能
更多詳情請參考
Properties,
Methods,
Subscripts,
Initialization,
Extensions,
Protocols.
類具有結(jié)構(gòu)所不具備的附加功能:
- 繼承使一個類能夠繼承另一個類的特征
- 類型轉(zhuǎn)換使您能夠在運行時檢查和解釋類實例的類型
- 反初始化器使類的實例能夠釋放它所分配的任何資源
- 引用計數(shù)允許對類實例的多個引用
更多詳情,請參考
Inheritance,
Type Casting,
Deinitialization,
Automatic Reference Counting.
類支持的附加功能是以增加復(fù)雜性為代價的吹截。作為一般的指導原則瘦陈,選擇結(jié)構(gòu)體,因為它們更容易推理波俄,并且在適當或必要時使用類晨逝。實際上,這意味著您定義的大多數(shù)自定義數(shù)據(jù)類型都是結(jié)構(gòu)體和枚舉懦铺。有關(guān)更詳細的比較捉貌,請參考 Choosing Between Structures and Classes.
Definition Syntax 定義的語法
定義結(jié)構(gòu)體
struct SomeStructure {
}
定義類
class SomeClass {
}
無論何時定義一個新結(jié)構(gòu)或類,都要定義一個新的Swift類型。任何類型名字都是大寫字母開頭趁窃,任何屬性名和方法名字都是小寫字母開頭
下面是一個結(jié)構(gòu)定義和類定義的例子:
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}
Structure and Class Instances 結(jié)構(gòu)體和類 的實例
Resolution結(jié)構(gòu)體定義和VideoMode類定義 只描述Resolution或VideoMode的樣子牧挣。它們本身并不描述特定的分辨率或視頻模式。為此醒陆,您需要創(chuàng)建結(jié)構(gòu)或類的實例瀑构。
對于結(jié)構(gòu)和類,創(chuàng)建實例的語法非常相似:
let someResolution = Resolution()
let someVideoMode = VideoMode()
結(jié)構(gòu)和類都對新實例使用初始化器語法统求。初始化器語法的最簡單形式是使用類或結(jié)構(gòu)的類型名检碗,后跟一對小括號,如Resolution()或VideoMode()码邻。這將創(chuàng)建類或結(jié)構(gòu)的新實例折剃,并將所有屬性初始化為其默認值。
類和結(jié)構(gòu)初始化更詳細的描述像屋,請參考 Initialization.
Accessing Properties 訪問屬性
可以使用點語法訪問實例的屬性怕犁。在點語法中,您可以在實例名之后立即編寫屬性名己莺,用句點(.)分隔奏甫,不使用任何空格:
print("The width of someResolution is \(someResolution.width)")
// Prints "The width of someResolution is 0"
在這個例子中,是一個解決方案凌受。width是someResolution的width屬性阵子,返回其默認初始值0。
您可以深入到子屬性中胜蛉,例如VideoMode的resolution屬性中的寬度屬性:
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"
Memberwise Initializers for Structure Types 結(jié)構(gòu)體帶成員初始器
所有結(jié)構(gòu)都有一個自動生成的成員初始化器挠进,您可以使用它初始化新結(jié)構(gòu)實例的成員屬性。新實例屬性的初始值可以通過名稱傳遞給memberwise初始化器:
let vga = Resolution(width: 640, height: 480)
與結(jié)構(gòu)體不同誊册,類不接受默認成員初始化领突。關(guān)于初始化更多信息,請參考 Initialization.
Structures and Enumerations Are Value Types 結(jié)構(gòu)和枚舉是值類型
值類型是這樣一種類型案怯,當它被賦值給一個變量或常量時君旦,或者當它被傳遞給一個函數(shù)時,它的值被復(fù)制嘲碱。
實際上金砍,在前幾章中,您已經(jīng)廣泛地使用了值類型麦锯。事實上捞魁,swift中的所有基本類型——整數(shù)、浮點數(shù)离咐、布爾值、字符串、數(shù)組和字典——都是值類型宵蛀,并且都是作為結(jié)構(gòu)體在底層實現(xiàn)的昆著。
所有結(jié)構(gòu)和枚舉都是Swift中的值類型。這意味著您創(chuàng)建的任何結(jié)構(gòu)和枚舉實例(以及它們作為屬性的任何值類型)在代碼中傳遞時都將被復(fù)制术陶。
注意:由標準庫(如數(shù)組凑懂、字典和字符串)定義的集合使用優(yōu)化來降低復(fù)制的性能成本。這些集合不是立即復(fù)制梧宫,而是共享元素存儲在原始實例和任何副本之間的內(nèi)存接谨。如果修改集合的一個副本,則在修改之前復(fù)制元素塘匣。您在代碼中看到的行為總是像立即發(fā)生了復(fù)制一樣脓豪。
考慮這個例子,它使用了前一個例子的分辨率結(jié)構(gòu):
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
本例聲明一個名為hd的常量忌卤,并將其設(shè)置為一個分辨率實例扫夜,該實例初始化為全高清視頻的寬度和高度(1920像素寬,1080像素高)驰徊。
然后笤闯,它聲明一個名為cinema的變量,并將其設(shè)置為hd的當前值棍厂。因為分辨率是一種結(jié)構(gòu)颗味,所以會生成現(xiàn)有實例的副本,并將這個新副本分配給cinema牺弹。盡管高清和電影現(xiàn)在有相同的寬度和高度浦马,但它們在幕后是兩個完全不同的例子。
下一步例驹,將影院寬度屬性修改為略寬的2K標準(2048像素寬捐韩,1080像素高)的寬度:
cinema.width = 2048
print("cinema is now \(cinema.width) pixels wide")
print("hd is still \(hd.width) pixels wide")
輸出結(jié)果 cinema 的 width 為 2048,hd 的width 為1920鹃锈。
當cinema被賦予當前的hd值時荤胁,hd中存儲的值被復(fù)制到新的cinema實例中。最終的結(jié)果是兩個完全獨立的實例屎债,其中包含相同的數(shù)值仅政。但是,由于它們是獨立的實例盆驹,所以將cinema的寬度設(shè)置為2048并不影響hd中存儲的寬度圆丹,如下圖所示:
在枚舉中也是這樣的:
enum CompassPoint {
case north, south, east, west
mutating func turnNorth() {
self = .north
}
}
var currentDirection = CompassPoint.west
let rememberedDirection = currentDirection
currentDirection.turnNorth()
print("The current direction is \(currentDirection)")
print("The remembered direction is \(rememberedDirection)")
currentDirection 為 north,rememberedDirection 為west躯喇。
Classes Are Reference Types 類是引用類型
與值類型不同辫封,引用類型在分配給變量或常量時硝枉,或傳遞給函數(shù)時,不會復(fù)制倦微。使用的不是副本妻味,而是對相同現(xiàn)有實例的引用。
下面是一個例子欣福,使用上面定義的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(tenEighty.frameRate)
tenEighty.frameRate值 為 30.0
注意责球,tenEighty 和alsoTenEighty 被聲明為常量,而不是變量拓劝。不過雏逾,你仍然可以改變它們的frameRate。因為tenEighty和alsoTenEighty常數(shù)本身的值實際上沒有變化郑临。tenEighty和alsoTenEighty本身都不“存儲”VideoMode實例——相反栖博,它們都在幕后引用一個VideoMode實例。改變的是底層VideoMode類型對象的frameRate屬性牧抵,而不是引用該VideoMode的常量 的值前普。
Identity Operators 身份操作符
因為類是引用類型冰评,所以多個常量和變量可以在幕后引用類的同一個實例。(對于結(jié)構(gòu)和枚舉則不一樣,因為當它們被分配給常量或變量余寥,或傳遞給函數(shù)時益涧,它們總是被復(fù)制软瞎。)
有時候闯割,找出兩個常量或變量是否引用了一個類的完全相同的實例是很有用的。為此省店,Swift提供了兩個身份操作符:
- === 引用同一個對象
- !== 引用的不是同一個對象
使用這些操作符檢查兩個常量或變量是否指向同一個實例:
if tenEighty === alsoTenEighty {
print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// Prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."
當您定義自己的自定義結(jié)構(gòu)和類時嚣崭,確定兩個對象相等性條件是您的責任。定義您自己的==和!=操作符實現(xiàn)的過程懦傍,請參考 Equivalence Operators雹舀。
Pointers 指針
如果您有使用C、c++或Objective-C的經(jīng)驗粗俱,您可能知道這些語言使用指針來引用內(nèi)存中的地址说榆。引用引用類型實例的Swift常量或變量類似于C中的指針,但不是指向內(nèi)存中的地址的直接指針寸认,并且不需要編寫星號(*)來表示正在創(chuàng)建引用签财。相反,這些引用像Swift中的任何其他常量或變量一樣定義偏塞。標準庫提供指針和緩沖區(qū)類型唱蒸,如果需要直接與指針交互,可以使用這些類型—請參閱手動內(nèi)存管理 Manual Memory Management灸叼。