一栽燕、類(lèi)和結(jié)構(gòu)體對(duì)比
- Swift 中類(lèi)和結(jié)構(gòu)體有很多共同點(diǎn)饶碘。共同處在于:
定義屬性用于存儲(chǔ)值
定義方法用于提供功能
定義下標(biāo)操作通過(guò)下標(biāo)語(yǔ)法可以訪(fǎng)問(wèn)它們的值
定義構(gòu)造器用于生成初始化值
通過(guò)擴(kuò)展以增加默認(rèn)實(shí)現(xiàn)的功能
遵循協(xié)議以提供某種標(biāo)準(zhǔn)功能
- 與結(jié)構(gòu)體相比稍刀,類(lèi)還有如下的附加功能:
繼承允許一個(gè)類(lèi)繼承另一個(gè)類(lèi)的特征
類(lèi)型轉(zhuǎn)換允許在運(yùn)行時(shí)檢查和解釋一個(gè)類(lèi)實(shí)例的類(lèi)型
析構(gòu)器允許一個(gè)類(lèi)實(shí)例釋放任何其所被分配的資源
引用計(jì)數(shù)允許對(duì)一個(gè)類(lèi)的多次引用
注意
結(jié)構(gòu)體總是通過(guò)被復(fù)制的方式在代碼中傳遞恬口,不使用引用計(jì)數(shù)表悬。
1弥锄、定義語(yǔ)法
- 類(lèi)和結(jié)構(gòu)體有著類(lèi)似的定義方式。我們通過(guò)關(guān)鍵字
class
和struct
來(lái)分別表示類(lèi)和結(jié)構(gòu)體蟆沫,并在一對(duì)大括號(hào)中定義它們的具體內(nèi)容:
class SomeClass {
// 在這里定義類(lèi)
}
struct SomeStructure {
// 在這里定義結(jié)構(gòu)體
}
注意:
請(qǐng)?jiān)诙x類(lèi)和結(jié)構(gòu)體時(shí), 使用首字母大寫(xiě)的駝峰命名法, 而在定義屬性和方法名的時(shí)候, 使用首字母小寫(xiě)的駝峰命名法
- 以下是定義結(jié)構(gòu)體和定義類(lèi)的示例:
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}
2籽暇、類(lèi)和結(jié)構(gòu)體實(shí)例
-
Resolution
結(jié)構(gòu)體和VideoMode
類(lèi)的定義僅描述了什么是Resolution
和VideoMode
。 - 它們并沒(méi)有描述一個(gè)特定的分辨率或者視頻模式饭庞。為了描述一個(gè)特定的分辨率或者視頻模式戒悠,我們需要生成一個(gè)它們的實(shí)例。
- 生成結(jié)構(gòu)體和類(lèi)實(shí)例的語(yǔ)法非常相似:
let someResolution = Resolution()
let someVideoMode = VideoMode()
- 結(jié)構(gòu)體和類(lèi)都使用構(gòu)造器語(yǔ)法來(lái)生成新的實(shí)例舟山。構(gòu)造器語(yǔ)法的最簡(jiǎn)單形式是在結(jié)構(gòu)體或者類(lèi)的類(lèi)型名稱(chēng)后跟隨一對(duì)空括號(hào)绸狐,如
Resolution()
或VideoMode()
。通過(guò)這種方式所創(chuàng)建的類(lèi)或者結(jié)構(gòu)體實(shí)例累盗,其屬性均會(huì)被初始化為默認(rèn)值寒矿。
3、屬性訪(fǎng)問(wèn)
- 通過(guò)使用點(diǎn)語(yǔ)法若债,你可以訪(fǎng)問(wèn)實(shí)例的屬性劫窒。其語(yǔ)法規(guī)則是,實(shí)例名后面緊跟屬性名拆座,兩者通過(guò)點(diǎn)號(hào)(
.
)連接:
print("The width of someResolution is \(someResolution.width)")
// 打印 "The width of someResolution is 0"
print("The width of someVideoMode is \(someVideoMode.resolution.width)")
// 打印 "The width of someVideoMode is 0"
- 你也可以使用點(diǎn)語(yǔ)法為變量屬性賦值:
someVideoMode.resolution.width = 1280
print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// 打印 "The width of someVideoMode is now 1280"
4、結(jié)構(gòu)體類(lèi)型的成員逐一構(gòu)造器
- 所有結(jié)構(gòu)體都有一個(gè)自動(dòng)生成的成員逐一構(gòu)造器冠息,用于初始化新結(jié)構(gòu)體實(shí)例中成員的屬性挪凑。新實(shí)例中各個(gè)屬性的初始值可以通過(guò)屬性的名稱(chēng)傳遞到成員逐一構(gòu)造器之中:
let vga = Resolution(width: 640, height: 480)
- 與結(jié)構(gòu)體不同,類(lèi)實(shí)例沒(méi)有默認(rèn)的成員逐一構(gòu)造器
二逛艰、結(jié)構(gòu)體和枚舉是值類(lèi)型
值類(lèi)型被賦予給一個(gè)變量躏碳、常量或者被傳遞給一個(gè)函數(shù)的時(shí)候,其值會(huì)被拷貝散怖。
在 Swift 中菇绵,所有的基本類(lèi)型:整數(shù)肄渗、浮點(diǎn)數(shù)、布爾值咬最、字符串翎嫡、數(shù)組和字典,都是值類(lèi)型永乌,并且在底層都是以結(jié)構(gòu)體的形式所實(shí)現(xiàn)惑申。
在 Swift 中,所有的結(jié)構(gòu)體和枚舉類(lèi)型都是值類(lèi)型翅雏。這意味著它們的實(shí)例圈驼,以及實(shí)例中所包含的任何值類(lèi)型屬性,在代碼中傳遞的時(shí)候都會(huì)被復(fù)制望几。
請(qǐng)看下面這個(gè)示例绩脆,其使用了前一個(gè)示例中的 Resolution 結(jié)構(gòu)體:
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.width = 2048
print("cinema is now \(cinema.width) pixels wide")
// 打印 "cinema is now 2048 pixels wide"
print("hd is still \(hd.width) pixels wide")
// 打印 "hd is still 1920 pixels wide"
在將
hd
賦予給cinema
的時(shí)候,實(shí)際上是將hd
中所存儲(chǔ)的值進(jìn)行拷貝橄抹,然后將拷貝的數(shù)據(jù)存儲(chǔ)到新的cinema
實(shí)例中靴迫。結(jié)果就是兩個(gè)完全獨(dú)立的實(shí)例碰巧包含有相同的數(shù)值。由于兩者相互獨(dú)立害碾,因此將cinema
的width
修改為2048
并不會(huì)影響hd
中的width
的值矢劲。枚舉也遵循相同的行為準(zhǔn)則:
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")
}
// 打印 "The remembered direction is still .West"
- 上例中
rememberedDirection
被賦予了currentDirection
的值,實(shí)際上它被賦予的是值的一個(gè)拷貝慌随。賦值過(guò)程結(jié)束后再修改currentDirection
的值并不影響rememberedDirection
所儲(chǔ)存的原始值的拷貝芬沉。
三、類(lèi)是引用類(lèi)型
與值類(lèi)型不同阁猜,引用類(lèi)型在被賦予到一個(gè)變量丸逸、常量或者被傳遞到一個(gè)函數(shù)時(shí),其值不會(huì)被拷貝剃袍。因此黄刚,引用的是已存在的實(shí)例本身而不是其拷貝。
請(qǐng)看下面這個(gè)示例民效,其使用了之前定義的
VideoMode
類(lèi):
let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0
- 然后憔维,tenEighty 被賦予名為 alsoTenEighty 的新常量,同時(shí)對(duì) alsoTenEighty 的幀率進(jìn)行修改:
let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0
- 因?yàn)轭?lèi)是引用類(lèi)型畏邢,所以
tenEight
和alsoTenEight
實(shí)際上引用的是相同的VideoMode
實(shí)例业扒。換句話(huà)說(shuō),它們是同一個(gè)實(shí)例的兩種叫法舒萎。
print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// 打印 "The frameRate property of theEighty is now 30.0"
1程储、恒等運(yùn)算符
因?yàn)轭?lèi)是引用類(lèi)型,有可能有多個(gè)常量和變量在幕后同時(shí)引用同一個(gè)類(lèi)實(shí)例。(對(duì)于結(jié)構(gòu)體和枚舉來(lái)說(shuō)章鲤,這并不成立摊灭。因?yàn)樗鼈冏鳛橹殿?lèi)型,在被賦予到常量败徊、變量或者傳遞到函數(shù)時(shí)帚呼,其值總是會(huì)被拷貝。)
如果能夠判定兩個(gè)常量或者變量是否引用同一個(gè)類(lèi)實(shí)例將會(huì)很有幫助集嵌。為了達(dá)到這個(gè)目的萝挤,Swift 內(nèi)建了兩個(gè)恒等運(yùn)算符:
等價(jià)于(===)
不等價(jià)于(!==)
- 運(yùn)用這兩個(gè)運(yùn)算符檢測(cè)兩個(gè)常量或者變量是否引用同一個(gè)實(shí)例:
if tenEighty === alsoTenEighty {
print("tenEighty and alsoTenEighty refer to the same Resolution instance.")
}
//打印 "tenEighty and alsoTenEighty refer to the same Resolution instance."
- 請(qǐng)注意,“等價(jià)于”(用三個(gè)等號(hào)表示根欧,
===
)與“等于”(用兩個(gè)等號(hào)表示怜珍,==
)的不同:
===: “等價(jià)于”表示兩個(gè)類(lèi)類(lèi)型(class type)的常量或者變量引用同一個(gè)類(lèi)實(shí)例。
==: “等于”表示兩個(gè)實(shí)例的值“相等”或“相同”凤粗, 判定時(shí)要遵照設(shè)計(jì)者定義的評(píng)判標(biāo)準(zhǔn)酥泛,
因此相對(duì)于“相等”來(lái)說(shuō),這是一種更加合適的叫法嫌拣。
- 當(dāng)你在定義你的自定義類(lèi)和結(jié)構(gòu)體的時(shí)候柔袁,你有義務(wù)來(lái)決定判定兩個(gè)實(shí)例“相等”的標(biāo)準(zhǔn)。
2异逐、指針
如果你有 C捶索,C++ 或者 Objective-C 語(yǔ)言的經(jīng)驗(yàn),那么你也許會(huì)知道這些語(yǔ)言使用指針來(lái)引用內(nèi)存中的地址灰瞻。一個(gè)引用某個(gè)引用類(lèi)型實(shí)例的 Swift 常量或者變量腥例,與 C 語(yǔ)言中的指針類(lèi)似,但是并不直接指向某個(gè)內(nèi)存地址酝润,也不要求你使用星號(hào)(*
)來(lái)表明你在創(chuàng)建一個(gè)引用燎竖。Swift 中的這些引用與其它的常量或變量的定義方式相同
四、類(lèi)和結(jié)構(gòu)體的選擇
在你的代碼中要销,你可以使用類(lèi)和結(jié)構(gòu)體來(lái)定義你的自定義數(shù)據(jù)類(lèi)型构回。
然而,結(jié)構(gòu)體實(shí)例總是通過(guò)值傳遞疏咐,類(lèi)實(shí)例總是通過(guò)引用傳遞纤掸。這意味兩者適用不同的任務(wù)。
按照通用的準(zhǔn)則浑塞,當(dāng)符合一條或多條以下條件時(shí)借跪,請(qǐng)考慮構(gòu)建結(jié)構(gòu)體:
數(shù)據(jù)結(jié)構(gòu)的主要目的是用來(lái)封裝少量相關(guān)簡(jiǎn)單數(shù)據(jù)值。
有理由預(yù)計(jì)該數(shù)據(jù)結(jié)構(gòu)的實(shí)例在被賦值或傳遞時(shí)缩举,封裝的數(shù)據(jù)將會(huì)被拷貝而不是被引用。
該數(shù)據(jù)結(jié)構(gòu)中儲(chǔ)存的值類(lèi)型屬性,也應(yīng)該被拷貝仅孩,而不是被引用托猩。
該數(shù)據(jù)結(jié)構(gòu)不需要去繼承另一個(gè)既有類(lèi)型的屬性或者行為。
- 舉例來(lái)說(shuō)辽慕,以下情境中適合使用結(jié)構(gòu)體:
幾何形狀的大小京腥,封裝一個(gè) width 屬性和 height 屬性,兩者均為 Double 類(lèi)型溅蛉。
一定范圍內(nèi)的路徑公浪,封裝一個(gè) start 屬性和 length 屬性,兩者均為 Int 類(lèi)型船侧。
三維坐標(biāo)系內(nèi)一點(diǎn)欠气,封裝 x,y 和 z 屬性镜撩,三者均為 Double 類(lèi)型预柒。
- 在所有其它案例中,定義一個(gè)類(lèi)袁梗,生成一個(gè)它的實(shí)例宜鸯,并通過(guò)引用來(lái)管理和傳遞。實(shí)際中遮怜,這意味著絕大部分的自定義數(shù)據(jù)構(gòu)造都應(yīng)該是類(lèi)淋袖,而非結(jié)構(gòu)體。
五锯梁、字符串即碗、數(shù)組、和字典類(lèi)型的賦值與復(fù)制行為
Swift 中涝桅,許多基本類(lèi)型拜姿,諸如 String
,Array
和 Dictionary
類(lèi)型均以結(jié)構(gòu)體的形式實(shí)現(xiàn)冯遂。這意味著被賦值給新的常量或變量蕊肥,或者被傳入函數(shù)或方法中時(shí),它們的值會(huì)被拷貝蛤肌。
Objective-C 中 NSString
壁却,NSArray
和 NSDictionary
類(lèi)型均以類(lèi)的形式實(shí)現(xiàn),而并非結(jié)構(gòu)體裸准。它們?cè)诒毁x值或者被傳入函數(shù)或方法時(shí)展东,不會(huì)發(fā)生值拷貝,而是傳遞現(xiàn)有實(shí)例的引用炒俱。
注意
以上是對(duì)字符串盐肃、數(shù)組爪膊、字典的“拷貝”行為的描述。在你的代碼中砸王,拷貝行為看起來(lái)似乎總會(huì)發(fā)生推盛。然而,Swift 在幕后只在絕對(duì)必要時(shí)才執(zhí)行實(shí)際的拷貝谦铃。Swift 管理所有的值拷貝以確保性能最優(yōu)化耘成,所以你沒(méi)必要去回避賦值來(lái)保證性能最優(yōu)化。