Swift tricks系列收集Swift牛逼的patterns和讓你代碼更加Swifty的tricks,持續更新中……
Associated Value
Swift的enum
和Object-C中的enum
一樣都不能擁有屬性,但是Swift提供了一個非常強大的功能——Associated Value。它可以讓enum
的case value
存儲不同類型的值。
引用蘋果官方的例子:
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
我們可以這樣創建Barcode
:
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
我們可以通過switch
語法將Associated Value取出來:
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
print("QR code: \(productCode).")
}
Raw Value
enum
可以通過raw value
對其進行預填充。例如,我們可以用一個Int類型的rawValue來表示硬幣的正反面:
enum Coin:Int{
case Head = 1
case Tail
}
Coin.Head.rawValue
為1,Coin.Tail.rawValue
為2。
我們也可以用String類型的rawValue來表示Icon:
我們甚至可以直接通過rawValue初始化enum
:
我們可以在定義enum
的時候指定rawValue的類型(一般會是Int, String, Character,Float等)。如果不指定,那么就沒有rawValue屬性。如下面的Coin.Head
就沒有rawValue。
enum case cannot have a raw value if the enum does not have a raw type
enum Coin{
case Head
case Tail
}
rawValue的本質是一個名為RawRepresentable
的protocol:
public protocol RawRepresentable {
associatedtype RawValue
public init?(rawValue: Self.RawValue)
public var rawValue: Self.RawValue { get }
}
Int, String, Character, Float等都實現了這個protocol。
下面我們會通過一個例子來深入理解Associated Value
和Raw Value
舉個??
假設我們在做一個學生的評分系統,我們需要一個enum
來表示學生的成績:
enum Score {
case Fail
case Pass
case Good
case Perfect
}
var input = 70;
let score : Score
if(input < 60){
score = .Fail
}else if(input < 80){
score = .Pass
}else if(input < 90){
score = .Good
}else{
score = .Perfect
}
上面的做法不怎么優雅,我們可以這樣做:
enum Score2 {
case Fail
case Pass
case Good
case Perfect
init(_ score : Int){
if(score < 60){
self = .Fail
}else if(score < 80){
self = .Pass
}else if(score < 90){
self = .Good
}else{
self = .Perfect
}
}
}
let score2 = Score2(85)
這樣一來,判斷邏輯就統一了,也更容易維護。
但是需求往往會比較復雜,例如老師在宣讀成績的時候需要寫評語,另外會表揚成績優秀的學生:
func writeComment(score : Int) -> () {
var comment : String
switch Score2(score) {
case .Fail:
comment = "表現不好,要努力啊!"
case .Pass:
comment = "加油!"
case .Good:
comment = "不錯!"
case .Perfect:
comment = "非常好!"
}
print("comment:\(comment), score:\(score)")
}
func applauseStudent(score : Int) -> () {
switch Score2(score) {
case .Perfect:
print("你是我的驕傲, 你考了\(score)")
default: break
}
}
func announceScore(score : Int) -> () {
writeComment(score)
applauseStudent(score)
}
writeComment
和applauseStudent
兩個方法都用到了score
和enum
,于是我們對同一個enum
初始化了兩次(雖然不是什么大事,但是我們還是要精益求精)。
改成下面這樣?
方案2
func writeComment(score : Int, grade : Score2) -> ()
func applauseStudent(score : Int, grade : Score2) -> ()
沒次傳兩個參數,解決了問題,但是有點ugly。有沒有更Swifty的方法?
方案3
我們可以用associated value
,將分數存儲在enum
中:
enum Score2 {
case Fail(Int)
case Pass(Int)
case Good(Int)
case Perfect(Int)
init(_ score : Int){
if(score < 60){
self = .Fail(score)
}else if(score < 80){
self = .Pass(score)
}else if(score < 90){
self = .Good(score)
}else{
self = .Perfect(score)
}
}
}
func writeComment(score : Score2) -> () {
var comment : String
var grade : Int
switch score {
case .Fail(let g):
comment = "表現不好,要努力啊!"
grade = g
case .Pass(let g):
comment = "加油!"
grade = g
case .Good(let g):
comment = "不錯!"
grade = g
case .Perfect(let g):
comment = "非常好!"
grade = g
}
print("comment:\(comment), score:\(grade)")
}
func applauseStudent(score : Score2) -> () {
switch score {
case .Perfect(let grade):
print("你是我的驕傲, 你考了\(grade)")
default: break
}
}
func announceScore(score : Int) -> () {
let s = Score2(score)
writeComment(s)
applauseStudent(s)
}
方案4
方案3看起來很不錯,但是我們在switch中做了大量的值提取操作。下面來介紹終極方案:associated value
+ raw value
enum Score3: RawRepresentable{
case Fail(Int)
case Pass(Int)
case Good(Int)
case Perfect(Int)
typealias RawValue = Int
var rawValue: RawValue {
var grade : Int
switch self {
case .Fail(let g):
grade = g
case .Pass(let g):
grade = g
case .Good(let g):
grade = g
case .Perfect(let g):
grade = g
}
return grade
}
init?(rawValue: RawValue) {
if(rawValue < 60){
self = .Fail(rawValue)
}else if(rawValue < 80){
self = .Pass(rawValue)
}else if(rawValue < 90){
self = .Good(rawValue)
}else{
self = .Perfect(rawValue)
}
}
}
func writeComment(score : Score3) -> () {
var comment : String
switch score {
case .Fail:
comment = "表現不好,要努力啊!"
case .Pass:
comment = "加油!"
case .Good:
comment = "不錯!"
case .Perfect:
comment = "非常好!"
}
print("comment:\(comment), score:\(score.rawValue)")
}
func applauseStudent(score : Score3) -> () {
switch score {
case .Perfect:
print("你是我的驕傲, 你考了\(score.rawValue)")
default: break
}
}
func announceScore(score : Int) -> () {
let s = Score3(rawValue: score)
writeComment(s!)
applauseStudent(s!)
}