Swift:類(lèi)和結(jié)構(gòu)體

中文文檔

一栽燕、類(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)鍵字 classstruct 來(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)的定義僅描述了什么是 ResolutionVideoMode
  • 它們并沒(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ú)立害碾,因此將 cinemawidth 修改為 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)型畏邢,所以 tenEightalsoTenEight 實(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)型拜姿,諸如 StringArrayDictionary 類(lèi)型均以結(jié)構(gòu)體的形式實(shí)現(xiàn)冯遂。這意味著被賦值給新的常量或變量蕊肥,或者被傳入函數(shù)或方法中時(shí),它們的值會(huì)被拷貝蛤肌。

Objective-C 中 NSString壁却,NSArrayNSDictionary 類(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)化。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末驹闰,一起剝皮案震驚了整個(gè)濱河市瘪菌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嘹朗,老刑警劉巖师妙,帶你破解...
    沈念sama閱讀 212,332評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異骡显,居然都是意外死亡疆栏,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,508評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)惫谤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)壁顶,“玉大人,你說(shuō)我怎么就攤上這事溜歪∪糇ǎ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,812評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵蝴猪,是天一觀(guān)的道長(zhǎng)调衰。 經(jīng)常有香客問(wèn)我,道長(zhǎng)自阱,這世上最難降的妖魔是什么嚎莉? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,607評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮沛豌,結(jié)果婚禮上趋箩,老公的妹妹穿的比我還像新娘。我一直安慰自己加派,他們只是感情好叫确,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,728評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著芍锦,像睡著了一般竹勉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上娄琉,一...
    開(kāi)封第一講書(shū)人閱讀 49,919評(píng)論 1 290
  • 那天次乓,我揣著相機(jī)與錄音吓歇,去河邊找鬼。 笑死票腰,一個(gè)胖子當(dāng)著我的面吹牛照瘾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播丧慈,決...
    沈念sama閱讀 39,071評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼主卫!你這毒婦竟也來(lái)了逃默?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,802評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤簇搅,失蹤者是張志新(化名)和其女友劉穎完域,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體瘩将,經(jīng)...
    沈念sama閱讀 44,256評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吟税,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,576評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了姿现。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肠仪。...
    茶點(diǎn)故事閱讀 38,712評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖备典,靈堂內(nèi)的尸體忽然破棺而出异旧,到底是詐尸還是另有隱情,我是刑警寧澤提佣,帶...
    沈念sama閱讀 34,389評(píng)論 4 332
  • 正文 年R本政府宣布吮蛹,位于F島的核電站,受9級(jí)特大地震影響拌屏,放射性物質(zhì)發(fā)生泄漏潮针。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,032評(píng)論 3 316
  • 文/蒙蒙 一倚喂、第九天 我趴在偏房一處隱蔽的房頂上張望每篷。 院中可真熱鬧,春花似錦务唐、人聲如沸雳攘。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)吨灭。三九已至,卻和暖如春刑巧,著一層夾襖步出監(jiān)牢的瞬間喧兄,已是汗流浹背无畔。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,026評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吠冤,地道東北人浑彰。 一個(gè)月前我還...
    沈念sama閱讀 46,473評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像拯辙,于是被迫代替她去往敵國(guó)和親郭变。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,606評(píng)論 2 350