Swift - 類和結(jié)構(gòu)體

類和結(jié)構(gòu)體


結(jié)構(gòu)體作為一種通用而又靈活的結(jié)構(gòu),成為了人們構(gòu)建代碼的基礎(chǔ)。你可以使用定義常量盾饮、變量和函數(shù)的語法,為你的結(jié)構(gòu)體和類定義屬性懒熙、添加方法丘损。

與其他編程語言所不同的是,Swift 并不要求你為自定義的結(jié)構(gòu)體和類的接口與實(shí)現(xiàn)代碼分別創(chuàng)建文件工扎。你只需在單一的文件中定義一個(gè)結(jié)構(gòu)體或者類徘钥,系統(tǒng)將會(huì)自動(dòng)生成面向其它代碼的外部接口。

注意

通常一個(gè)的實(shí)例被稱為對象肢娘。然而相比其他語言呈础,Swift 中結(jié)構(gòu)體和類的功能更加相近,本章中所討論的大部分功能都可以用在結(jié)構(gòu)體或者類上橱健。因此,這里會(huì)使用實(shí)例這個(gè)更通用的術(shù)語拘荡。

結(jié)構(gòu)體和類對比

Swift 中結(jié)構(gòu)體和類有很多共同點(diǎn)臼节。兩者都可以:

  • 定義屬性用于存儲(chǔ)值

  • 定義方法用于提供功能

  • 定義下標(biāo)操作用于通過下標(biāo)語法訪問它們的值

  • 定義構(gòu)造器用于設(shè)置初始值

  • 通過擴(kuò)展以增加默認(rèn)實(shí)現(xiàn)之外的功能

  • 遵循協(xié)議以提供某種標(biāo)準(zhǔn)功能

與結(jié)構(gòu)體相比,類還有如下的附加功能:

  • 繼承允許一個(gè)類繼承另一個(gè)類的特征

  • 類型轉(zhuǎn)換允許在運(yùn)行時(shí)檢查和解釋一個(gè)類實(shí)例的類型

  • 析構(gòu)器允許一個(gè)類實(shí)例釋放任何其所被分配的資源

  • 引用計(jì)數(shù)允許對一個(gè)類的多次引用

類支持的附加功能是以增加復(fù)雜性為代價(jià)的。作為一般準(zhǔn)則犀盟,優(yōu)先使用結(jié)構(gòu)體纽哥,因?yàn)樗鼈兏菀桌斫猓瑑H在適當(dāng)或必要時(shí)才使用類吼句。實(shí)際上锅必,這意味著你的大多數(shù)自定義數(shù)據(jù)類型都會(huì)是結(jié)構(gòu)體和枚舉。更多詳細(xì)的比較參見 在結(jié)構(gòu)和類之間進(jìn)行選擇命辖。

注意

類和 actors 共享很多特性况毅。更多信息請參見 并發(fā)

類型定義的語法

結(jié)構(gòu)體和類有著相似的定義方式尔艇。你通過 struct 關(guān)鍵字引入結(jié)構(gòu)體尔许,通過 class 關(guān)鍵字引入類,并將它們的具體定義放在一對大括號中:

struct SomeStructure {
    // 在這里定義結(jié)構(gòu)體
}
class SomeClass {
    // 在這里定義類
}

注意

每當(dāng)你定義一個(gè)新的結(jié)構(gòu)體或者類時(shí)终娃,你都是定義了一個(gè)新的 Swift 類型味廊。請使用 UpperCamelCase 這種方式來命名類型(如這里的 SomeClassSomeStructure),以便符合標(biāo)準(zhǔn) Swift 類型的大寫命名風(fēng)格(如 String棠耕,IntBool)余佛。請使用 lowerCamelCase 這種方式來命名屬性和方法(如 frameRateincrementCount),以便和類型名區(qū)分窍荧。

以下是定義結(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?
}

在上面的示例中定義了一個(gè)名為 Resolution 的結(jié)構(gòu)體辉巡,用來描述基于像素的分辨率。這個(gè)結(jié)構(gòu)體包含了名為 widthheight 的兩個(gè)存儲(chǔ)屬性蕊退。存儲(chǔ)屬性是與結(jié)構(gòu)體或者類綁定的郊楣,并存儲(chǔ)在其中的常量或變量。當(dāng)這兩個(gè)屬性被初始化為整數(shù) 0 的時(shí)候瓤荔,它們會(huì)被推斷為 Int 類型净蚤。

在上面的示例還定義了一個(gè)名為 VideoMode 的類,用來描述視頻顯示器的某個(gè)特定視頻模式输硝。這個(gè)類包含了四個(gè)可變的存儲(chǔ)屬性今瀑。第一個(gè), resolution,被初始化為一個(gè)新的 Resolution 結(jié)構(gòu)體的實(shí)例橘荠,屬性類型被推斷為 Resolution屿附。新 VideoMode 實(shí)例同時(shí)還會(huì)初始化其它三個(gè)屬性,它們分別是初始值為 falseinterlaced(意為“非隔行視頻”)砾医,初始值為 0.0frameRate拿撩,以及值為可選 Stringname衣厘。因?yàn)?name 是一個(gè)可選類型如蚜,它會(huì)被自動(dòng)賦予一個(gè)默認(rèn)值 nil,意為“沒有 name 值”影暴。

結(jié)構(gòu)體和類的實(shí)例

Resolution 結(jié)構(gòu)體和 VideoMode 類的定義僅描述了什么是 ResolutionVideoMode错邦。它們并沒有描述一個(gè)特定的分辨率(resolution)或者視頻模式(video mode)。為此型宙,你需要?jiǎng)?chuàng)建結(jié)構(gòu)體或者類的一個(gè)實(shí)例撬呢。

創(chuàng)建結(jié)構(gòu)體和類實(shí)例的語法非常相似:

let someResolution = Resolution()
let someVideoMode = VideoMode()

結(jié)構(gòu)體和類都使用構(gòu)造器語法來創(chuàng)建新的實(shí)例。構(gòu)造器語法的最簡單形式是在結(jié)構(gòu)體或者類的類型名稱后跟隨一對空括號妆兑,如 Resolution()VideoMode()魂拦。通過這種方式所創(chuàng)建的類或者結(jié)構(gòu)體實(shí)例,其屬性均會(huì)被初始化為默認(rèn)值搁嗓。構(gòu)造過程 章節(jié)會(huì)對類和結(jié)構(gòu)體的初始化進(jìn)行更詳細(xì)的討論芯勘。

屬性訪問

你可以通過使用點(diǎn)語法訪問實(shí)例的屬性。其語法規(guī)則是腺逛,實(shí)例名后面緊跟屬性名荷愕,兩者以點(diǎn)號(.)分隔,不帶空格:

print("The width of someResolution is \(someResolution.width)")
// 打印 "The width of someResolution is 0"

在上面的例子中棍矛,someResolution.width 引用 someResolutionwidth 屬性安疗,返回 width 的初始值 0

你也可以訪問子屬性够委,如 VideoModeresolution 屬性的 width 屬性:

print("The width of someVideoMode is \(someVideoMode.resolution.width)")
// 打印 "The width of someVideoMode is 0"

你也可以使用點(diǎn)語法為可變屬性賦值:

someVideoMode.resolution.width = 1280
print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// 打印 "The width of someVideoMode is now 1280"

結(jié)構(gòu)體類型的成員逐一構(gòu)造器

所有結(jié)構(gòu)體都有一個(gè)自動(dòng)生成的成員逐一構(gòu)造器荐类,用于初始化新結(jié)構(gòu)體實(shí)例中成員的屬性。新實(shí)例中各個(gè)屬性的初始值可以通過屬性的名稱傳遞到成員逐一構(gòu)造器之中:

let vga = Resolution(width: 640, height: 480)

與結(jié)構(gòu)體不同茁帽,類實(shí)例沒有默認(rèn)的成員逐一構(gòu)造器玉罐。構(gòu)造過程 章節(jié)會(huì)對構(gòu)造器進(jìn)行更詳細(xì)的討論。

結(jié)構(gòu)體和枚舉是值類型

值類型是這樣一種類型脐雪,當(dāng)它被賦值給一個(gè)變量厌小、常量或者被傳遞給一個(gè)函數(shù)的時(shí)候,其值會(huì)被拷貝战秋。

在之前的章節(jié)中璧亚,你已經(jīng)大量使用了值類型。實(shí)際上,Swift 中所有的基本類型:整數(shù)(integer)癣蟋、浮點(diǎn)數(shù)(floating-point number)透硝、布爾值(boolean)、字符串(string)疯搅、數(shù)組(array)和字典(dictionary)濒生,都是值類型,其底層也是使用結(jié)構(gòu)體實(shí)現(xiàn)的幔欧。

Swift 中所有的結(jié)構(gòu)體和枚舉類型都是值類型罪治。這意味著它們的實(shí)例,以及實(shí)例中所包含的任何值類型的屬性礁蔗,在代碼中傳遞的時(shí)候都會(huì)被復(fù)制觉义。

注意

標(biāo)準(zhǔn)庫定義的集合,例如數(shù)組浴井,字典和字符串晒骇,都對復(fù)制進(jìn)行了優(yōu)化以降低性能成本。新集合不會(huì)立即復(fù)制磺浙,而是跟原集合共享同一份內(nèi)存洪囤,共享同樣的元素。在集合的某個(gè)副本要被修改前撕氧,才會(huì)復(fù)制它的元素瘤缩。而你在代碼中看起來就像是立即發(fā)生了復(fù)制。

請看下面這個(gè)示例呵曹,其使用了上一個(gè)示例中的 Resolution 結(jié)構(gòu)體:

let hd = Resolution(width: 1920, height: 1080)
var cinema = hd

在以上示例中款咖,聲明了一個(gè)名為 hd 的常量,其值為一個(gè)初始化為全高清視頻分辨率(1920 像素寬奄喂,1080 像素高)的 Resolution 實(shí)例铐殃。

然后示例中又聲明了一個(gè)名為 cinema 的變量,并將 hd 賦值給它跨新。因?yàn)?Resolution 是一個(gè)結(jié)構(gòu)體富腊,所以會(huì)先創(chuàng)建一個(gè)現(xiàn)有實(shí)例的副本,然后將副本賦值給 cinema 域帐。盡管 hdcinema 有著相同的寬(width)和高(height)赘被,但是在幕后它們是兩個(gè)完全不同的實(shí)例。

下面肖揣,為了符合數(shù)碼影院放映的需求(2048 像素寬民假,1080 像素高),cinemawidth 屬性被修改為稍微寬一點(diǎn)的 2K 標(biāo)準(zhǔn):

cinema.width = 2048

查看 cinemawidth 屬性龙优,它的值確實(shí)改為了 2048

print("cinema is now  \(cinema.width) pixels wide")
// 打印 "cinema is now 2048 pixels wide"

然而羊异,初始的 hd 實(shí)例中 width 屬性還是 1920

print("hd is still \(hd.width) pixels wide")
// 打印 "hd is still 1920 pixels wide"

hd 賦值給 cinema 時(shí),hd 中所存儲(chǔ)的會(huì)拷貝到新的 cinema 實(shí)例中。結(jié)果就是兩個(gè)完全獨(dú)立的實(shí)例包含了相同的數(shù)值野舶。由于兩者相互獨(dú)立易迹,因此將 cinemawidth 修改為 2048 并不會(huì)影響 hd 中的 width 的值,如下圖所示:

image.png

枚舉也遵循相同的行為準(zhǔn)則:

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)")
// 打印 "The current direction is north"
// 打印 "The remembered direction is west"

當(dāng) rememberedDirection 被賦予了 currentDirection 的值平道,實(shí)際上它被賦予的是值的一個(gè)拷貝睹欲。賦值過程結(jié)束后再修改 currentDirection 的值并不影響 rememberedDirection 所儲(chǔ)存的原始值的拷貝。

類是引用類型

與值類型不同一屋,引用類型在被賦予到一個(gè)變量窘疮、常量或者被傳遞到一個(gè)函數(shù)時(shí),其值不會(huì)被拷貝陆淀。因此考余,使用的是已存在實(shí)例的引用先嬉,而不是其拷貝轧苫。

請看下面這個(gè)示例,其使用了之前定義的 VideoMode 類:

let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0

以上示例中疫蔓,聲明了一個(gè)名為 tenEighty 的常量含懊,并讓其引用一個(gè) VideoMode 類的新實(shí)例。它的視頻模式(video mode)被賦值為之前創(chuàng)建的 HD 分辨率(1920*1080)的一個(gè)拷貝衅胀。然后將它設(shè)置為隔行視頻岔乔,名字設(shè)為 “1080i”,并將幀率設(shè)置為 25.0 幀每秒滚躯。

接下來雏门,將 tenEighty 賦值給一個(gè)名為 alsoTenEighty 的新常量,并修改 alsoTenEighty 的幀率:

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

因?yàn)轭愂且妙愋偷停?tenEightalsoTenEight 實(shí)際上引用的是同一個(gè) VideoMode 實(shí)例茁影。換句話說,它們是同一個(gè)實(shí)例的兩種叫法丧凤,如下圖所示:

image.png

通過查看 tenEightyframeRate 屬性募闲,可以看到它正確地顯示了底層的 VideoMode 實(shí)例的新幀率 30.0

print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// 打印 "The frameRate property of theEighty is now 30.0"

這個(gè)例子也顯示了為何引用類型更加難以理解。如果 tenEightyalsoTenEighty 在你代碼中的位置相距很遠(yuǎn)愿待,那么就很難找到所有修改視頻模式的地方浩螺。無論在哪使用 tenEighty,你都要考慮使用 alsoTenEighty 的代碼仍侥,反之亦然要出。相反,值類型就更容易理解了农渊,因?yàn)槟愕脑创a中與同一個(gè)值交互的代碼都很近患蹂。

需要注意的是 tenEightyalsoTenEighty 被聲明為常量而不是變量。然而你依然可以改變 tenEighty.frameRatealsoTenEighty.frameRate,這是因?yàn)?tenEightyalsoTenEighty 這兩個(gè)常量的值并未改變况脆。它們并不“存儲(chǔ)”這個(gè) VideoMode 實(shí)例饭宾,而僅僅是對 VideoMode 實(shí)例的引用。所以格了,改變的是底層 VideoMode 實(shí)例的 frameRate 屬性看铆,而不是指向 VideoMode 的常量引用的值。

恒等運(yùn)算符

因?yàn)轭愂且妙愋褪⒛远鄠€(gè)常量和變量可能在幕后同時(shí)引用同一個(gè)類實(shí)例弹惦。(對于結(jié)構(gòu)體和枚舉來說,這并不成立悄但。因?yàn)樗鼈冏鳛橹殿愋吞囊诒毁x予到常量、變量或者傳遞到函數(shù)時(shí)檐嚣,其值總是會(huì)被拷貝助泽。)

判定兩個(gè)常量或者變量是否引用同一個(gè)類實(shí)例有時(shí)很有用。為了達(dá)到這個(gè)目的嚎京,Swift 提供了兩個(gè)恒等運(yùn)算符:

  • 相同(===

  • 不相同(!==

使用這兩個(gè)運(yùn)算符檢測兩個(gè)常量或者變量是否引用了同一個(gè)實(shí)例:

if tenEighty === alsoTenEighty {
    print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// 打印 "tenEighty and alsoTenEighty refer to the same VideoMode instance."

請注意嗡贺,“相同”(用三個(gè)等號表示,===)與“等于”(用兩個(gè)等號表示鞍帝,==)的不同诫睬。“相同”表示兩個(gè)類類型(class type)的常量或者變量引用同一個(gè)類實(shí)例帕涌∩惴玻“等于”表示兩個(gè)實(shí)例的值“相等”或“等價(jià)”,判定時(shí)要遵照設(shè)計(jì)者定義的評判標(biāo)準(zhǔn)蚓曼。

當(dāng)在定義你的自定義結(jié)構(gòu)體和類的時(shí)候亲澡,你有義務(wù)來決定判定兩個(gè)實(shí)例“相等”的標(biāo)準(zhǔn)。在章節(jié) 等價(jià)操作符 中將會(huì)詳細(xì)介紹實(shí)現(xiàn)自定義 == 和 != 運(yùn)算符的流程辟躏。

指針

如果你有 C谷扣,C++ 或者 Objective-C 語言的經(jīng)驗(yàn),那么你也許會(huì)知道這些語言使用指針來引用內(nèi)存中的地址捎琐。Swift 中引用了某個(gè)引用類型實(shí)例的常量或變量会涎,與 C 語言中的指針類似,不過它并不直接指向某個(gè)內(nèi)存地址瑞凑,也不要求你使用星號(*)來表明你在創(chuàng)建一個(gè)引用末秃。相反,Swift 中引用的定義方式與其它的常量或變量的一樣籽御。如果需要直接與指針交互练慕,你可以使用標(biāo)準(zhǔn)庫提供的指針和緩沖區(qū)類型 —— 參見 手動(dòng)管理內(nèi)存惰匙。


繼續(xù)閱讀 Swift - 屬性

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市铃将,隨后出現(xiàn)的幾起案子秧骑,更是在濱河造成了極大的恐慌耗帕,老刑警劉巖括眠,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件刨沦,死亡現(xiàn)場離奇詭異,居然都是意外死亡悯仙,警方通過查閱死者的電腦和手機(jī)龄毡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來锡垄,“玉大人沦零,你說我怎么就攤上這事』趿耄” “怎么了路操?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長茴她。 經(jīng)常有香客問我寻拂,道長,這世上最難降的妖魔是什么丈牢? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮瞄沙,結(jié)果婚禮上己沛,老公的妹妹穿的比我還像新娘。我一直安慰自己距境,他們只是感情好申尼,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著垫桂,像睡著了一般师幕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上诬滩,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天霹粥,我揣著相機(jī)與錄音,去河邊找鬼疼鸟。 笑死后控,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的空镜。 我是一名探鬼主播浩淘,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼捌朴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了张抄?” 一聲冷哼從身側(cè)響起砂蔽,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎署惯,沒想到半個(gè)月后察皇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡泽台,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年什荣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片怀酷。...
    茶點(diǎn)故事閱讀 40,110評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡稻爬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蜕依,到底是詐尸還是另有隱情桅锄,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布样眠,位于F島的核電站友瘤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏檐束。R本人自食惡果不足惜辫秧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望被丧。 院中可真熱鬧盟戏,春花似錦、人聲如沸甥桂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽黄选。三九已至蝇摸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間办陷,已是汗流浹背貌夕。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留懂诗,地道東北人蜂嗽。 一個(gè)月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像殃恒,于是被迫代替她去往敵國和親植旧。 傳聞我的和親對象是個(gè)殘疾皇子辱揭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 前言類和結(jié)構(gòu)體是人們構(gòu)建代碼所用的一種通用且靈活的構(gòu)造體。我們可以使用完全相同的語法規(guī)則來為類和結(jié)構(gòu)體定義屬性(常...
    Mr_yinwei閱讀 764評論 0 1
  • 中文文檔 一病附、類和結(jié)構(gòu)體對比 Swift 中類和結(jié)構(gòu)體有很多共同點(diǎn)问窃。共同處在于: 與結(jié)構(gòu)體相比,類還有如下的附加功...
    伯wen閱讀 218評論 0 0
  • Swift創(chuàng)建類和結(jié)構(gòu)體時(shí),不需要將聲明文件和實(shí)現(xiàn)文件分開 一個(gè)類的實(shí)例通常被稱為對象覆积。 結(jié)構(gòu)體和類對比 共同點(diǎn)都...
    Sunday_David閱讀 303評論 0 0
  • 在面向過程的語言中,要想實(shí)現(xiàn)類似類的功能只能借助結(jié)構(gòu)體,其實(shí)從OC源碼也能看出來,類的組成本就是復(fù)雜的結(jié)構(gòu)體實(shí)現(xiàn)的...
    如風(fēng)如花不如你閱讀 8,215評論 2 6
  • 類與結(jié)構(gòu)體的比較 看到相同點(diǎn)的時(shí)候我在想听皿,確定不需要把枚舉一起拉進(jìn)來比較嘛 : ) 結(jié)構(gòu)體在傳值的時(shí)候永遠(yuǎn)都是進(jìn)行...
    Counting_S閱讀 442評論 0 2