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

結(jié)構(gòu)體和類是通用的、靈活的結(jié)體聋涨,它們成為程序代碼的構(gòu)建部分。我們可以使用定義常量负乡、變量和函數(shù)的相同語(yǔ)法來定義屬性和方法牍白,以在結(jié)構(gòu)體和類中添加功能。

與其他編程語(yǔ)言不同抖棘,Swift不需要為自定義結(jié)構(gòu)體和類創(chuàng)建單獨(dú)的接口和實(shí)現(xiàn)文件茂腥。在Swift中,我們?cè)趩蝹€(gè)文件中定義結(jié)構(gòu)體或類切省,該類或結(jié)構(gòu)體的外部接口將自動(dòng)提供給其他代碼使用最岗。

注意
類的實(shí)例傳統(tǒng)上被稱為對(duì)象。但是朝捆,Swift的結(jié)構(gòu)體和類般渡,在功能性上比在其他語(yǔ)言中更接近,本章的大部分內(nèi)容描述了應(yīng)用于類或結(jié)構(gòu)體類型實(shí)例的功能芙盘。因此驯用,使用術(shù)語(yǔ)實(shí)例更為通用。

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

Swift中的結(jié)構(gòu)體和類有許多共同之處儒老。兩者都可以:

  • 定義屬性來存儲(chǔ)值蝴乔;
  • 定義提供功能的方法;
  • 定義下標(biāo)贷盲,以使用下標(biāo)語(yǔ)法訪問其值淘这;
  • 定義初始值設(shè)定項(xiàng)以設(shè)置初始狀態(tài);
  • 擴(kuò)展以擴(kuò)展其功能巩剖,使其功能超出默認(rèn)實(shí)現(xiàn)铝穷;
  • 符合協(xié)議,以提供某種標(biāo)準(zhǔn)功能

有關(guān)詳細(xì)信息佳魔,請(qǐng)參見屬性曙聂、方法下標(biāo)鞠鲜、初始化宁脊、擴(kuò)展協(xié)議

類具有結(jié)構(gòu)體不具備的其他功能:

  • 繼承贤姆,使一個(gè)類能夠繼承另一個(gè)類的特性榆苞。
  • 類型轉(zhuǎn)換,使我們能夠在運(yùn)行時(shí)檢查和解釋類實(shí)例的類型霞捡。
  • 釋放實(shí)例坐漏,使類的實(shí)例能夠釋放它分配的任何資源。
  • 引用計(jì)數(shù),允許對(duì)類實(shí)例進(jìn)行多個(gè)引用赊琳。
    有關(guān)詳細(xì)信息街夭,請(qǐng)參見繼承類型轉(zhuǎn)換躏筏、釋放實(shí)例自動(dòng)引用計(jì)數(shù)板丽。

類支持的額外功能,是增加其復(fù)雜性為代價(jià)的趁尼。作為一個(gè)一般的指南埃碱,更偏向使用結(jié)構(gòu)體,因?yàn)樗鼈兏菀淄评砣蹩ǎ⑶以谶m當(dāng)或必要時(shí)使用類乃正。實(shí)際上,這意味著我們定義的大多數(shù)自定義數(shù)據(jù)類型將是結(jié)構(gòu)體和枚舉婶博。有關(guān)更詳細(xì)的比較瓮具,請(qǐng)參見結(jié)構(gòu)體和類之間的選擇

定義語(yǔ)法

結(jié)構(gòu)體和類具有類似的定義語(yǔ)法凡人。引入帶有struct關(guān)鍵字的結(jié)構(gòu)體和帶有class關(guān)鍵字的類名党。兩者都將其整個(gè)定義放在一對(duì)大括號(hào)中:

struct SomeStructure {
    // structure definition goes here
}
class SomeClass {
    // class definition goes here
}

注意
無論何時(shí)定義新的結(jié)構(gòu)體或類,都要定義一個(gè)新的Swift類型挠轴。為類型提供大寫名稱(如SomeStructureSomeClass)以匹配標(biāo)準(zhǔn)Swift類型(如String传睹、IntBool)的大小寫。為屬性和方法提供小寫的melcase名稱(如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?
}

上面的示例定義了一種稱為Resolution的新結(jié)構(gòu)體,以描述基于像素的顯示分辨率启上。此結(jié)構(gòu)體有兩個(gè)存儲(chǔ)屬性邢隧,稱為widthheight。存儲(chǔ)屬性是作為結(jié)構(gòu)體或類的一部分捆綁和存儲(chǔ)的常量或變量冈在。通過將這兩個(gè)屬性設(shè)置為0的初始整數(shù)值倒慧,可以推斷它們屬于Int類型。

上面的示例還定義了一個(gè)名為VideoMode的新類包券,用于描述視頻顯示的特定視頻模式纫谅。這個(gè)類有四個(gè)變量存儲(chǔ)屬性。第一個(gè)是resolution溅固,它用一個(gè)新的Resolution結(jié)構(gòu)體實(shí)例初始化付秕,該實(shí)例推斷出resolution的屬性類型。對(duì)于其他三個(gè)屬性侍郭,新的VideoMode實(shí)例將使用interlaced設(shè)置false(表示“非插入視頻”)盹牧、frameRate為0.0和名為name的可選字符串值進(jìn)行初始化俩垃。name屬性會(huì)自動(dòng)指定一個(gè)默認(rèn)值nil或“no name value”励幼,因?yàn)樗强蛇x類型汰寓。

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

Resolution結(jié)構(gòu)體定義和VideoMode類定義僅描述ResolutionVideoMode的外觀。它們本身并不描述特定的分辨率或視頻模式苹粟。為此有滑,需要?jiǎng)?chuàng)建結(jié)構(gòu)體或類的實(shí)例。
對(duì)于結(jié)構(gòu)體和類嵌削,創(chuàng)建實(shí)例的語(yǔ)法非常相似:

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

結(jié)構(gòu)體和類都對(duì)新實(shí)例使用初始值設(shè)定項(xiàng)語(yǔ)法毛好。初始化器語(yǔ)法的最簡(jiǎn)單形式是使用類或結(jié)構(gòu)體的類型名,后跟空括號(hào)苛秕,如Resolution()VideoMode()肌访。這將創(chuàng)建類或結(jié)構(gòu)體的新實(shí)例,并將所有屬性初始化為其默認(rèn)值艇劫。類和結(jié)構(gòu)體初始化在初始化中有更詳細(xì)的描述吼驶。

訪問屬性

可以使用點(diǎn)語(yǔ)法訪問實(shí)例的屬性。在點(diǎn)語(yǔ)法中店煞,屬性名緊跟在實(shí)例名之后蟹演,用句點(diǎn)(.)分隔,不帶空格:

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

在這個(gè)例子中顷蟀,someResolution.width引用width屬性酒请,并返回其默認(rèn)初始值0。

我們可以深入到子屬性鸣个,例如VideoModeresolution屬性中的width屬性:

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

也可以使用點(diǎn)語(yǔ)法為變量屬性指定新值:

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

結(jié)構(gòu)體類型的成員式初始化方法

所有結(jié)構(gòu)體都有一個(gè)自動(dòng)生成的成員式初始化方法羞反,我們可以使用它初始化新結(jié)構(gòu)體實(shí)例的成員屬性。新實(shí)例屬性的初始值可以通過名稱傳遞給成員式初始化方法:

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

與結(jié)構(gòu)體不同囤萤,類實(shí)例不接收默認(rèn)的成員式初始化方法昼窗。初始化方法在初始化中有更詳細(xì)的描述。

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

值類型是一種類型阁将,它的值在賦值給變量或常量或傳遞給函數(shù)時(shí)被復(fù)制膏秫。

在前面的章節(jié)中,我們已經(jīng)廣泛地使用了值類型做盅。事實(shí)上缤削,Swift的整數(shù)、浮點(diǎn)數(shù)吹榴、布爾亭敢、字符串、數(shù)組和字典中的所有基本類型都是值類型图筹,并在系統(tǒng)中實(shí)現(xiàn)為結(jié)構(gòu)體帅刀。

所有結(jié)構(gòu)體和枚舉都是Swift中的值類型让腹。這意味著我們創(chuàng)建的任何結(jié)構(gòu)體和枚舉實(shí)例以及它們作為屬性的任何值類型在代碼中傳遞時(shí)都會(huì)被復(fù)制。

注意
由標(biāo)準(zhǔn)庫(kù)定義的集合(如數(shù)組扣溺、字典和字符串)使用優(yōu)化來降低復(fù)制的性能成本骇窍。這些集合不是立即創(chuàng)建副本,而是共享存儲(chǔ)在原始實(shí)例和任何副本之間的元素的內(nèi)存锥余。如果修改了集合的一個(gè)副本腹纳,則將在修改之前復(fù)制元素。我們?cè)诖a中看到的行為總是好像一個(gè)拷貝立即發(fā)生驱犹。

考慮這個(gè)例子嘲恍,它使用上一個(gè)例子中的Resolution結(jié)構(gòu)體:

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

本例聲明了一個(gè)名為hd的常量,并將其設(shè)置為以全高清視頻的寬度和高度(1920像素寬×1080像素高)來初始化的Resolution實(shí)例雄驹。

然后聲明一個(gè)名為cinema的變量佃牛,并將其設(shè)置為hd的當(dāng)前值。因?yàn)?code>Resolution是一個(gè)結(jié)構(gòu)體医舆,所以將生成現(xiàn)有實(shí)例的副本俘侠,并將此新副本指定給cinema。盡管高清和影院現(xiàn)在有相同的寬度和高度彬向,但它們?cè)谀缓髤s是兩個(gè)完全不同的例子兼贡。

接下來,Resolutionwidth屬性被修改為用于數(shù)字電影院投影的略寬的2K標(biāo)準(zhǔn)的寬度(2048像素寬和1080像素高):

cinema.width = 2048

檢查cinemawidth屬性表明它確實(shí)已更改為2048:

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

但是娃胆,原始hd實(shí)例的width屬性仍然具有舊值1920:

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

當(dāng)給cinema以當(dāng)前的hd值時(shí)遍希,存儲(chǔ)在hd中的值被復(fù)制到新的cinema實(shí)例中。最終結(jié)果是兩個(gè)完全獨(dú)立的實(shí)例里烦,其中包含相同的數(shù)值凿蒜。但是,由于它們是獨(dú)立的實(shí)例胁黑,將cinema寬度設(shè)置為2048不會(huì)影響hd中存儲(chǔ)的寬度废封,如下圖所示:

截屏2021-04-06 下午9.41.34.png

同樣的行為也適用于枚舉:

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

當(dāng)membereddirection被指定currentDirection的值時(shí),它實(shí)際上被設(shè)置為該值的一個(gè)副本丧蘸。此后更改currentDirection的值不會(huì)影響存儲(chǔ)在rememberedDirection中的原始值的副本漂洋。

類是引用類型

與值類型不同,當(dāng)引用類型被賦給變量或常量力喷,或者被傳遞給函數(shù)時(shí)刽漂,它們不會(huì)被復(fù)制。使用對(duì)同一現(xiàn)有實(shí)例的引用弟孟,而不是副本贝咙。

下面是一個(gè)示例,使用上面定義的VideoMode類:

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

本例聲明了一個(gè)名為tenEighty的新常量拂募,并將其設(shè)置為引用VideoMode類的新實(shí)例庭猩。視頻模式從之前被分配了1920×1080的HD分辨率的副本窟她。它被設(shè)置為隔行掃描,其名稱被設(shè)置為“1080i”蔼水,其幀速率被設(shè)置為每秒25.0幀震糖。

接下來,tenEighty被分配給一個(gè)新的常量徙缴,稱為alsotenance试伙,并且alsotenance的幀速率被修改:

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

因?yàn)轭愂且妙愋停?code>tenEighty和alsoTenEighty實(shí)際上都引用同一個(gè)VideoMode實(shí)例于样。實(shí)際上,它們只是同一個(gè)實(shí)例的兩個(gè)不同名稱潘靖,如下圖所示:

截屏2021-04-06 下午9.43.19.png

檢查tenEightyframeRate屬性表明穿剖,它可以從VideoMode實(shí)例正確報(bào)告30.0的新幀速率:

print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// Prints "The frameRate property of tenEighty is now 30.0"

這個(gè)例子還展示了引用類型如何更難推理。如果tenEightyalsoTenEighty在程序代碼中相距很遠(yuǎn)卦溢,則很難找到更改視頻模式的所有方法糊余。無論在哪里使用tenEighty,都必須考慮使用alsoTenEighty的代碼单寂,反之亦然贬芥。相比之下,值類型更容易推理宣决,因?yàn)榕c相同值交互的所有代碼在源文件中都很接近蘸劈。

請(qǐng)注意,tenEightyalsoTenEighty被聲明為常量尊沸,而不是變量威沫。但是,我們?nèi)匀豢梢愿淖?code>tenEighty.frameRate以及alsoTenEighty.frameRate洼专。因?yàn)?code>tenEighty和alsoTenEighty常量本身的值實(shí)際上并沒有改變棒掠。tenEightyalsoTenEighty本身并不“存儲(chǔ)”VideoMode實(shí)例,而是在幕后引用VideoMode實(shí)例屁商。改變的是底層視頻模式的frameRate屬性烟很,而不是該視頻模式的常量引用值。

標(biāo)識(shí)運(yùn)算符

因?yàn)轭愂且妙愋屠猓远鄠€(gè)常量和變量有可能在幕后引用同一個(gè)類的單個(gè)實(shí)例雾袱。(對(duì)于結(jié)構(gòu)體和枚舉,情況并非如此帽哑,因?yàn)楫?dāng)它們被賦給常量谜酒、變量或傳遞給函數(shù)時(shí),它們總是被復(fù)制妻枕。)

有時(shí)僻族,找出兩個(gè)常量或變量是否引用了一個(gè)類的完全相同的實(shí)例會(huì)很有用粘驰。為了實(shí)現(xiàn)這一點(diǎn),Swift提供了兩個(gè)身份操作符:

  • 等同于(===
  • 不等同于(!==)
    使用這些運(yùn)算符檢查兩個(gè)常量或變量是否引用同一個(gè)實(shí)例:
if tenEighty === alsoTenEighty {
    print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// Prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."

請(qǐng)注意述么,等同于(由三個(gè)等號(hào)表示蝌数,或===)并不是相等(由兩個(gè)等號(hào)表示,或==)度秘。等同于表示類 類型的兩個(gè)常量或變量引用完全相同的類實(shí)例顶伞。根據(jù)類型設(shè)計(jì)者的定義,對(duì)于相等的某些適當(dāng)含義剑梳,相等表示兩個(gè)實(shí)例在值上被認(rèn)為是相等的或相等的唆貌。

當(dāng)我們自定義結(jié)構(gòu)體和類時(shí),我們的責(zé)任是確定兩個(gè)實(shí)例是否相等垢乙。定義自己的==!=運(yùn)算符的實(shí)現(xiàn)的過程在等價(jià)運(yùn)算符中描述锨咙。

指針

如果我們有C、C++或Objto-C的經(jīng)驗(yàn)追逮,我們可能知道這些語(yǔ)言使用指針來引用內(nèi)存中的地址酪刀。引用某個(gè)類型實(shí)例的Swift常量或變量類似于C中的指針,但不是指向內(nèi)存中地址的直接指針钮孵,也不需要寫星號(hào)(*)來表示正在創(chuàng)建引用骂倘。相反,這些引用的定義與Swift中的任何其他常量或變量一樣巴席。標(biāo)準(zhǔn)庫(kù)提供了指針和緩沖區(qū)類型历涝,如果需要直接與指針交互,可以使用這些類型請(qǐng)參見手動(dòng)內(nèi)存管理情妖。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末睬关,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子毡证,更是在濱河造成了極大的恐慌电爹,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件料睛,死亡現(xiàn)場(chǎng)離奇詭異丐箩,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)恤煞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門屎勘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人居扒,你說我怎么就攤上這事概漱。” “怎么了喜喂?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵瓤摧,是天一觀的道長(zhǎng)竿裂。 經(jīng)常有香客問我,道長(zhǎng)照弥,這世上最難降的妖魔是什么腻异? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮这揣,結(jié)果婚禮上悔常,老公的妹妹穿的比我還像新娘。我一直安慰自己给赞,他們只是感情好机打,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著塞俱,像睡著了一般姐帚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上障涯,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音膳汪,去河邊找鬼唯蝶。 笑死,一個(gè)胖子當(dāng)著我的面吹牛遗嗽,可吹牛的內(nèi)容都是我干的粘我。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼痹换,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼征字!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起娇豫,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤匙姜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后冯痢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體氮昧,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡笑旺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年掂恕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棋电。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡振劳,死狀恐怖椎组,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情历恐,我是刑警寧澤寸癌,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布专筷,位于F島的核電站,受9級(jí)特大地震影響灵份,放射性物質(zhì)發(fā)生泄漏仁堪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一填渠、第九天 我趴在偏房一處隱蔽的房頂上張望弦聂。 院中可真熱鬧,春花似錦氛什、人聲如沸莺葫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)捺檬。三九已至,卻和暖如春贸铜,著一層夾襖步出監(jiān)牢的瞬間堡纬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工蒿秦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留烤镐,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓棍鳖,卻偏偏與公主長(zhǎng)得像炮叶,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子渡处,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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

  • 結(jié)構(gòu)體和類 結(jié)構(gòu)體和類作為一種通用而又靈活的結(jié)構(gòu)镜悉,成為了人們構(gòu)建代碼的基礎(chǔ)。你可以使用定義常量医瘫、變量和函數(shù)的語(yǔ)法侣肄,...
    Longshihua閱讀 543評(píng)論 0 1
  • swift中,我們?cè)谝粋€(gè)文件中定義結(jié)構(gòu)體和類并實(shí)現(xiàn)他的接口登下。 基礎(chǔ)知識(shí) 結(jié)構(gòu)體和類的比較 相同點(diǎn): 屬性:保存數(shù)據(jù)...
    枯樹戀閱讀 1,480評(píng)論 0 0
  • 一茫孔、類和結(jié)構(gòu)體的對(duì)比 (一)、類和結(jié)構(gòu)體的相同點(diǎn) 定義存儲(chǔ)值的屬性被芳; 定義提供功能的方法缰贝; 定義下標(biāo)以使用下標(biāo)語(yǔ)法...
    WSJay閱讀 1,688評(píng)論 2 4
  • swift中結(jié)構(gòu)體與類有著密切的關(guān)系。通常他們都能:1.定義屬性用于存儲(chǔ)值畔濒,定義方法提供功能2.定義構(gòu)造器生成初始...
    曲年閱讀 4,516評(píng)論 0 4
  • 一剩晴、結(jié)構(gòu)體 在Swift標(biāo)準(zhǔn)庫(kù)中,絕大多數(shù)的公開類都是結(jié)構(gòu)體,比如:Bool赞弥、Int毅整、Double 、String...
    冷武橘閱讀 69評(píng)論 0 0