Swift學習筆記(四)--枚舉,類與結構體

枚舉(Enumerations)

枚舉在Swift里面得到了很大的拓展, 使其變得更加簡單, 易用且強大.

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

獲得枚舉值也是很簡單, 而且蘋果還特意進行了一些簡化:

var directionToHead = CompassPoint.West
directionToHead = .East // 根據類型就已經知道了East就是CompassPoint的East

如果你夠好奇的話, 肯定已經猜到了這個簡化還是類型推導的功勞, 下面的代碼可以驗證:

var directionToHead: CompassPoint = .East  
  1. 用Switch語句來匹配枚舉值(Matching Enumeration Values with a Switch Statement)
    直接看代碼應該會更清晰:
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, 
// 因為我們定義的枚舉是可以窮舉的, 所以全部寫上就不會報錯了, 
// 個人還是建議對枚舉沒有特殊要求不要加default, 這樣編譯器會強制我們處理所有的分支,
// 如果以后加了一個枚舉值, 而且這個枚舉用的比較多, 可以由編譯器進行提醒
  1. 關聯值(Associated Values)
    簡單說來, 就是Swift里面的枚舉并不單單是一個值可以, 它還可以關聯(我感覺叫存儲還容易理解些)一些別的值, 比如WWDC里面的例子:
enum TrainStatus {
    case OnTime
    case Delay(Int) // 晚點了幾分鐘
}
var status = TrainStatus.Delay(10) // 晚點了10分鐘

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

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


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

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


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

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


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

直接看官網的例子吧:

enum ArithmeticExpression {
case Number(Int)
// 如果去掉indirect就會報錯, 提示你加入
indirect case Addition(ArithmeticExpression, ArithmeticExpression)
indirect case Multiplication(ArithmeticExpression, ArithmeticExpression)
}
// 當然, 多個indirect是可以簡化的, 直接寫在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"


這個東西, 一般用的可能不多, 但是要用的時候要是發現編不過還是很郁悶的, 順便提一句, Swift里面貌似不能用struct來實現鏈表了, 試過給struct加indirect會報錯, 哈哈, 不過可以用class...如果有方法可以用struct實現, 請一定要指點我一下...

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

enum CompassPoint: Float {
var description:String {
return "value is (self)" // 運算屬性分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"

想象力豐富的同學肯定想到了, 雖然不能有存儲屬性, 但是我們可以有關聯值, 從某種程度上來代替存儲屬性來實現一些方案也是可以的.


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



###類與結構體(Classes and Structures)
這一章只對類和結構體進行一些簡單的介紹, 著重會介紹蘋果對使用類還是結構體的一些指導意見.

1. 類與結構體對比(Comparing Classes and Structures)
二者都能實現的:
a. 定義存儲屬性
b. 定義方法
c. 定義下標(subscripts)來訪問它們的值
d. 定義初始化方法
e. 可以拓展默認實現
f. 可以實現協議

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

2. 基本語法
和其它語言沒有多大區別, 直接看例子吧:

//定義類和結構體:
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}
// 獲取二者實例:
let someResolution = Resolution()
let someVideoMode = VideoMode()
// 訪問屬性:
print("The width of someResolution is (someResolution.width)")
print("The width of someVideoMode is (someVideoMode.resolution.width)")
// 設置屬性:
someVideoMode.resolution.width = 1280
print("The width of someVideoMode is now (someVideoMode.resolution.width)")

// 結構體的成員初始化函數:
let vga = Resolution(width: 640, height: 480) // 編譯器自動合成

3. 結構體和枚舉值是值類型:
用例子可以證明之:

// 結構體:
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. 類是引用類型
這個例子也不需要舉了, 肯定是這樣的, 主要講下接下來的2個細節吧:
a. 全等操作符(===), 也有叫恒等的: 引入這個東西還是因為沒有指針啊, 所以需要一個操作符來比較2個實例是不是同一個東西, 所以就有了===. 這個和相等操作符的區別是, 相等操作符會比較其內容, 而不是地址.

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

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

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

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

對蘋果的聲明, 我做了一個測試, 可惜并沒有得到預想的結果, 不知道是不是測試的姿勢不對, 也可能是沒開啟優化選項? 測試代碼和結果如下:

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("======")

上面的代碼, 如果是有優化的話, 上面6個String應該只用一份就夠了, 但是我得到的結果是這樣的:

(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塊區域被復用了, 而且只有一次, 說明內存里面是有4份一樣是String(如果不算"123"這個全局變量), 如果有正確的測試姿勢, 請回復一下哈. (誤!)
// 2016-3-17更正:
上面用unsafeAddressOf打出不同的地址應該是和String實現的方式有關, 實際上真正字符串數據存儲的地方在String._core._baseAddress(在Debug模式下, 展開String可以看到), 因此上面這個例子不能說明蘋果沒有優化, 實際上, 對于值類型, 蘋果的原則是"copy on write", 也就是說, 只有在真正修改的時候才會執行copy操作, 可以通過下面的代碼進行驗證:

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


好了, 結構體和類的基本概念就到這, 明天出差, 之后再更新咯~~~
細節參考[官方文檔](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html#//apple_ref/doc/uid/TP40014097-CH13-ID82)
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容