Swift學(xué)習(xí)筆記(四)--枚舉,類與結(jié)構(gòu)體

枚舉(Enumerations)

枚舉在Swift里面得到了很大的拓展, 使其變得更加簡(jiǎn)單, 易用且強(qiáng)大.

  1. 枚舉語法(Enumeration Syntax)
    與ObjC一樣, 枚舉通過enum來聲明, 例如:
enum CompassPoint {
    case North
    case South
    case East
    case West
}
// 如果想簡(jiǎn)單點(diǎn), 寫成一行也是可以的
enum Planet {
    case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}

獲得枚舉值也是很簡(jiǎn)單, 而且蘋果還特意進(jìn)行了一些簡(jiǎn)化:

var directionToHead = CompassPoint.West
directionToHead = .East // 根據(jù)類型就已經(jīng)知道了East就是CompassPoint的East

如果你夠好奇的話, 肯定已經(jīng)猜到了這個(gè)簡(jiǎn)化還是類型推導(dǎo)的功勞, 下面的代碼可以驗(yàn)證:

var directionToHead: CompassPoint = .East  
  1. 用Switch語句來匹配枚舉值(Matching Enumeration Values with a Switch Statement)
    直接看代碼應(yīng)該會(huì)更清晰:
directionToHead = .South
switch directionToHead {
case .North:
    print("Lots of planets have a north")
case .South:
    print("Watch out for penguins")
case .East:
    print("Where the sun rises")
case .West:
    print("Where the skies are blue")
}
// 之前說過的, switch必須要涵蓋全部范圍, 所以除了枚舉外, 一般都要有default, 
// 因?yàn)槲覀兌x的枚舉是可以窮舉的, 所以全部寫上就不會(huì)報(bào)錯(cuò)了, 
// 個(gè)人還是建議對(duì)枚舉沒有特殊要求不要加default, 這樣編譯器會(huì)強(qiáng)制我們處理所有的分支,
// 如果以后加了一個(gè)枚舉值, 而且這個(gè)枚舉用的比較多, 可以由編譯器進(jìn)行提醒
  1. 關(guān)聯(lián)值(Associated Values)
    簡(jiǎn)單說來, 就是Swift里面的枚舉并不單單是一個(gè)值可以, 它還可以關(guān)聯(lián)(我感覺叫存儲(chǔ)還容易理解些)一些別的值, 比如WWDC里面的例子:
enum TrainStatus {
    case OnTime
    case Delay(Int) // 晚點(diǎn)了幾分鐘
}
var status = TrainStatus.Delay(10) // 晚點(diǎn)了10分鐘

那么假如要取這個(gè)值怎么辦呢? 繼續(xù)看:
```
switch status {
case .OnTime:
print("The train is ontime")
case .Delay(let minutes):
print("Delayed (minutes) minutes")

}
// 如果有多個(gè)參數(shù), 常規(guī)寫法是.Delay(let minutes, let station, let reason):
// 所以蘋果又幫我們簡(jiǎn)化了, 可以直接 case let .Delay(minutes, station, reason):


4. 原始值(Raw Values)
和別的語言不同, Swift不會(huì)對(duì)默認(rèn)對(duì)枚舉進(jìn)行一個(gè)"賦初值"的動(dòng)作, 也就是說, CompassPoint.North和0并不是天然綁定的, 除非我們自己手動(dòng)寫上. 例如:

enum CompassPoint: Int { // 注意這里要寫類型了的
case North = 1 // 后面的不寫就遞增, 當(dāng)然了, 僅限數(shù)值類型的(Double, Float也行)
case South // 第一個(gè)都不寫就是0
case East // 如果是String的話, 默認(rèn)就是其字面值, 如.South就是"South"
case West // 其它類型都是全部指定原始值
}


如果有了原始值, 那么我們就能通過原始值來獲取枚舉了, 例如:

let direction = CompassPoint(rawValue: 1) // .North
// 注意:如果你的rawValue超過了枚舉范圍, 就會(huì)返回nil, 所以這個(gè)方式初始化出來的枚舉值是Optional的, 使用前要拆包


5. 遞歸枚舉(Recursive Enumerations)
聽起來貌似很負(fù)責(zé)的樣子, 為此蘋果又來了一個(gè)關(guān)鍵字叫, indirect case(必須搭配case或者enum使用, 單獨(dú)一個(gè)indirect是不成關(guān)鍵字的). 其實(shí)它的本質(zhì)就是如果我在枚舉里面還要使用這個(gè)枚舉類型怎么辦? 回顧一下C語言實(shí)現(xiàn)鏈表的時(shí)候, struct Node里面還有struct Node, 所有我們的next要用struct Node *的形式. 同樣的, 這里的枚舉也會(huì)遇到這個(gè)問題, 但是Swift已經(jīng)沒有指針了, 自然就要引入新的關(guān)鍵字了.

直接看官網(wǎng)的例子吧:

enum ArithmeticExpression {
case Number(Int)
// 如果去掉indirect就會(huì)報(bào)錯(cuò), 提示你加入
indirect case Addition(ArithmeticExpression, ArithmeticExpression)
indirect case Multiplication(ArithmeticExpression, ArithmeticExpression)
}
// 當(dāng)然, 多個(gè)indirect是可以簡(jiǎn)化的, 直接寫在enum前面, 告知一下
indirect enum ArithmeticExpression {
case Number(Int)
case Addition(ArithmeticExpression, ArithmeticExpression)
case Multiplication(ArithmeticExpression, ArithmeticExpression)
}


使用起來的大概是這樣的, 感受一下:

func evaluate(expression: ArithmeticExpression) -> Int {
switch expression {
case .Number(let value):
return value
case .Addition(let left, let right):
return evaluate(left) + evaluate(right)
case .Multiplication(let left, let right):
return evaluate(left) * evaluate(right)
}
}
// evaluate (5 + 4) * 2
let five = ArithmeticExpression.Number(5)
let four = ArithmeticExpression.Number(4)
let sum = ArithmeticExpression.Addition(five, four)
let product = ArithmeticExpression.Multiplication(sum, ArithmeticExpression.Number(2))
print(evaluate(product))
// prints "18"


這個(gè)東西, 一般用的可能不多, 但是要用的時(shí)候要是發(fā)現(xiàn)編不過還是很郁悶的, 順便提一句, Swift里面貌似不能用struct來實(shí)現(xiàn)鏈表了, 試過給struct加indirect會(huì)報(bào)錯(cuò), 哈哈, 不過可以用class...如果有方法可以用struct實(shí)現(xiàn), 請(qǐng)一定要指點(diǎn)我一下...

6. 枚舉可以有運(yùn)算屬性(Computed Properties)和函數(shù)
在Swift里面, 屬性分為兩種, 一種叫存儲(chǔ)屬性(Store Properties), 其實(shí)就是我們一般的屬性, 一種叫運(yùn)算屬性, 二者的區(qū)別, 我總結(jié)起來就是看有沒有實(shí)體, 如果有實(shí)體的話, 就是存儲(chǔ)屬性, 否則就是運(yùn)算屬性, 舉個(gè)例子, 很多類,結(jié)構(gòu)體或者枚舉的description都是運(yùn)算屬性:

enum CompassPoint: Float {
var description:String {
return "value is (self)" // 運(yùn)算屬性分get和set, 目前這種寫法是get, 以后再講set
}
case North
case South
case East
case West
func desc() {
print("value is (self)")
}
}
var cp: CompassPoint = .South
cp.description // 打印出"value is South"
cp.desc() // 打印出"value is South\n"

想象力豐富的同學(xué)肯定想到了, 雖然不能有存儲(chǔ)屬性, 但是我們可以有關(guān)聯(lián)值, 從某種程度上來代替存儲(chǔ)屬性來實(shí)現(xiàn)一些方案也是可以的.


好了枚舉差不多到這里, 具體細(xì)節(jié)參考[官方文檔](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html#//apple_ref/doc/uid/TP40014097-CH12-ID145)



###類與結(jié)構(gòu)體(Classes and Structures)
這一章只對(duì)類和結(jié)構(gòu)體進(jìn)行一些簡(jiǎn)單的介紹, 著重會(huì)介紹蘋果對(duì)使用類還是結(jié)構(gòu)體的一些指導(dǎo)意見.

1. 類與結(jié)構(gòu)體對(duì)比(Comparing Classes and Structures)
二者都能實(shí)現(xiàn)的:
a. 定義存儲(chǔ)屬性
b. 定義方法
c. 定義下標(biāo)(subscripts)來訪問它們的值
d. 定義初始化方法
e. 可以拓展默認(rèn)實(shí)現(xiàn)
f. 可以實(shí)現(xiàn)協(xié)議

和大多數(shù)語言一樣, 下面是只有類能實(shí)現(xiàn)的:
a. 繼承
b. 類型轉(zhuǎn)換允許你在運(yùn)行時(shí)檢查和解釋實(shí)例類型
c. 析構(gòu)方法(Deinitializers)允許你在釋放類實(shí)例持有的一些資源
d. 引用計(jì)數(shù)

2. 基本語法
和其它語言沒有多大區(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?
}
// 獲取二者實(shí)例:
let someResolution = Resolution()
let someVideoMode = VideoMode()
// 訪問屬性:
print("The width of someResolution is (someResolution.width)")
print("The width of someVideoMode is (someVideoMode.resolution.width)")
// 設(shè)置屬性:
someVideoMode.resolution.width = 1280
print("The width of someVideoMode is now (someVideoMode.resolution.width)")

// 結(jié)構(gòu)體的成員初始化函數(shù):
let vga = Resolution(width: 640, height: 480) // 編譯器自動(dòng)合成

3. 結(jié)構(gòu)體和枚舉值是值類型:
用例子可以證明之:

// 結(jié)構(gòu)體:
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.width = 2048
print("cinema is now (cinema.width) pixels wide")
// prints "cinema is now 2048 pixels wide"
print("hd is still (hd.width) pixels wide")
// prints "hd is still 1920 pixels wide"

// 枚舉:
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")
}
// prints "The remembered direction is still .West"


4. 類是引用類型
這個(gè)例子也不需要舉了, 肯定是這樣的, 主要講下接下來的2個(gè)細(xì)節(jié)吧:
a. 全等操作符(===), 也有叫恒等的: 引入這個(gè)東西還是因?yàn)闆]有指針啊, 所以需要一個(gè)操作符來比較2個(gè)實(shí)例是不是同一個(gè)東西, 所以就有了===. 這個(gè)和相等操作符的區(qū)別是, 相等操作符會(huì)比較其內(nèi)容, 而不是地址.

b. 蘋果特意解釋了一些關(guān)于指針的事情: Swift中指向引用類型的常/變量和C語言的指針很類似, 但是卻不是直接指向內(nèi)存中的地址, 并且我們也不需要寫*號(hào)來表示我們要?jiǎng)?chuàng)建一個(gè)引用類型, 引用類型和Swift里面的其它類型的定義是基本一致的. (感覺說了等于沒說, 其實(shí)就是告訴我們, 沒指針了)

5. 類與結(jié)構(gòu)體的選擇(Choosing Between Classes and Structures)
蘋果給出了一系列的指導(dǎo)意見, 如果滿足下面的一個(gè)或者多個(gè)條件, 就考慮用結(jié)構(gòu)體吧:
a. 這個(gè)結(jié)構(gòu)的主要目的是為了封裝一些相對(duì)簡(jiǎn)單的數(shù)據(jù)
b. 這個(gè)結(jié)構(gòu)值被賦值或者被傳遞的時(shí)候, 它被復(fù)制是合理的
c. 這個(gè)結(jié)構(gòu)內(nèi)部的存儲(chǔ)屬性類型也是值類型, 并且它們也期望被復(fù)制而不是被引用
d. 這個(gè)結(jié)構(gòu)不需要從其它類型中集成屬性

下面是一些例子:
a. 幾何圖形: 可能只會(huì)封裝width和height, 它們都是Double類型的
b. 表示范圍: 可能只會(huì)封裝start和length, 它們都是Int類型的
c. 3D坐標(biāo)系: 可能只會(huì)封裝x,y,z 它們都是Double類型的

6. 字符串,數(shù)組和字典的賦值和復(fù)制(Assignment and Copy Behavior for Strings, Arrays, and Dictionaries)
如之前所說, 這三種類型都是值類型, 查看一下定義就知道它們是用結(jié)構(gòu)體實(shí)現(xiàn)的. 需要注意的是, 雖說這三個(gè)值類型在我們的代碼中總是會(huì)被復(fù)制的, 然而, Swift會(huì)很智能地只在真正需要復(fù)制的時(shí)候才會(huì)復(fù)制. Swift會(huì)管理所有的值復(fù)制來優(yōu)化性能, 所以我們不需要不敢賦值, 唯恐影響性能.

對(duì)蘋果的聲明, 我做了一個(gè)測(cè)試, 可惜并沒有得到預(yù)想的結(jié)果, 不知道是不是測(cè)試的姿勢(shì)不對(duì), 也可能是沒開啟優(yōu)化選項(xiàng)? 測(cè)試代碼和結(jié)果如下:

let str = "123"
let str2 = str
print("(str)(str2)")
let str3 = str
let str4 = str
print("(str3)
(str4)")
let str5 = str
let str6 = str
print("(str5)_(str6)")
print("======")

上面的代碼, 如果是有優(yōu)化的話, 上面6個(gè)String應(yīng)該只用一份就夠了, 但是我得到的結(jié)果是這樣的:

(lldb) po unsafeAddressOf("123")
? 0x00007fdd58519f10
- pointerValue : 140588646244112

(lldb) po unsafeAddressOf(str)
? 0x00007fdd5860fba0
- pointerValue : 140588647250848 // a

(lldb) po unsafeAddressOf(str2)
? 0x00007fdd5860fba0
- pointerValue : 140588647250848 // a

(lldb) po unsafeAddressOf(str3)
? 0x00007fdd5b9669b0
- pointerValue : 140588701084080 // b

(lldb) po unsafeAddressOf(str4)
? 0x00007fdd5b9669b0
- pointerValue : 140588701084080 // b

(lldb) po unsafeAddressOf(str5)
? 0x00007fdd5b96a310

  • pointerValue : 140588701098768

(lldb) po unsafeAddressOf(str6)
? 0x00007fdd58401b70

  • pointerValue : 140588645096304
從地址可以看出來, 6份按道理同樣的字符串, 只有2塊區(qū)域被復(fù)用了, 而且只有一次, 說明內(nèi)存里面是有4份一樣是String(如果不算"123"這個(gè)全局變量), 如果有正確的測(cè)試姿勢(shì), 請(qǐng)回復(fù)一下哈. (誤!)
// 2016-3-17更正:
上面用unsafeAddressOf打出不同的地址應(yīng)該是和String實(shí)現(xiàn)的方式有關(guān), 實(shí)際上真正字符串?dāng)?shù)據(jù)存儲(chǔ)的地方在String._core._baseAddress(在Debug模式下, 展開String可以看到), 因此上面這個(gè)例子不能說明蘋果沒有優(yōu)化, 實(shí)際上, 對(duì)于值類型, 蘋果的原則是"copy on write", 也就是說, 只有在真正修改的時(shí)候才會(huì)執(zhí)行copy操作, 可以通過下面的代碼進(jìn)行驗(yàn)證:

let str = "123"
var str2 = str // 這個(gè)時(shí)候可以看到str2._core._baseAddress與str1的相同, 直到...
str2.appendContentsOf("4") // str2._core._baseAddress被修改了


好了, 結(jié)構(gòu)體和類的基本概念就到這, 明天出差, 之后再更新咯~~~
細(xì)節(jié)參考[官方文檔](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html#//apple_ref/doc/uid/TP40014097-CH13-ID82)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蜡饵,一起剝皮案震驚了整個(gè)濱河市道宅,隨后出現(xiàn)的幾起案子评抚,更是在濱河造成了極大的恐慌谦秧,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搞莺,死亡現(xiàn)場(chǎng)離奇詭異息罗,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)才沧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門迈喉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人温圆,你說我怎么就攤上這事挨摸。” “怎么了岁歉?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵得运,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我锅移,道長(zhǎng)熔掺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任非剃,我火速辦了婚禮置逻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘备绽。我一直安慰自己券坞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布肺素。 她就那樣靜靜地躺著恨锚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪压怠。 梳的紋絲不亂的頭發(fā)上眠冈,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天飞苇,我揣著相機(jī)與錄音菌瘫,去河邊找鬼。 笑死布卡,一個(gè)胖子當(dāng)著我的面吹牛雨让,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播忿等,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼栖忠,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起庵寞,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤狸相,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后捐川,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脓鹃,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年古沥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瘸右。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡岩齿,死狀恐怖太颤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情盹沈,我是刑警寧澤龄章,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站乞封,受9級(jí)特大地震影響瓦堵,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜歌亲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一菇用、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧陷揪,春花似錦惋鸥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至飞蚓,卻和暖如春滤港,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背趴拧。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工溅漾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人著榴。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓添履,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親脑又。 傳聞我的和親對(duì)象是個(gè)殘疾皇子暮胧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

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