Swift鋒芒畢露 無腦意譯

Cruise_Chan發表於2014-11-06

前言

作者自己說自己很喜歡swift,因為他喜歡Haskell。可能看上了swift支援函數語言程式設計的緣故。

中間扯皮各種略。。。

扯到函式程式設計剛開始不習慣但是會帶來方便。。略結束。。。

栗子

ex by oc:

- (NSAttributedString *)attributedString:(NSString *)input 
{
    return [[NSAttributedString alloc] initWithString:input];
}

看上去無公害,但是引數如果是nil,那邊會導致崩潰。而且更坑爹是在執行時才會發現這個問題。這種問題一旦是你的下游程式猿(使用者)去接手的時候,找這個BUG就不是很好找了咯。

對比Swift寫法:

extension NSAttributedString {
    init(string str: String)
}

如果可以傳入一個nil值得引數,那麼介面會變成這樣:

extension NSAttributedString {
    init(string str: String?)
}

這種寫法的優勢在於更強的解釋性,也省去了看文件的時間。更不會導致執行時錯誤。

建議

儘量避免無用選項關鍵字。用選項關鍵字是確實需要才使用的,比如你的引數是顏色,顏色需要符合某種要求。如果此時你的引數不滿足這種要求那麼你希望返回nil:

func parseColorFromHexString(input: String) -> UIColor? {
    // ...
}

列舉

列舉是swift得新特性,與OC的列舉有顯著的不同。OC中的列舉說白了就是整型。

讓我們考慮下布林型別。我們只需要考慮真或假。
在選項關鍵字也是這樣的原理,也只有兩種情況:有值跟nil.swift中選項與布林同樣都用列舉定義。不同的是選項列舉是帶繫結值得。我們來看下各自的定義:

enum Boolean {
    case False
    case True
}

enum Optional<A> {
    case Nil
    case Some(A)
}

二選一列舉經常在函數語言程式設計中用於你在兩件事物中進行選擇的場景。舉個例子,如果你想返回一個整型或者錯誤,你可以使用Either<Int, NSError>。如果你要存一個布林或者字串的字典,你可以使用Either<Bool, String>作為Key值。

清楚的知道什麼時候該用列舉什麼時候不該用列舉有一點難度,當我們為一組相近型別得資料建立結構的時候。栗子,如果我們用swift來封裝Github的介面,我們需要把各種暴露出的介面用列舉型來表示。為了獲得使用者頭像,我們提供使用者名稱。獲取使用者版本庫我們提供了使用者名稱跟,排序布林值。

enum Github {
    case Zen
    case UserProfile(String)
    case Repositories(username: String, sortAscending: Bool)
}

定義開發性介面使用列舉真是棒極了。介面列表是受限的,我們可以定義各類介面。如果遺漏了某個列舉值我們會接受到一個告警。如果我們擅自新增了一類情況,我們需要更新所有用到該類列舉型別得程式碼。

其他使用列舉的時候不能隨意新增case,除非有程式碼許可權。這是一個非常好的限制。假想下你可以新增一個情形到Bool或者Optional,那麼你所有用來它得方程都得重寫。

讓我們拿貨幣轉換器做個栗子:

enum Currency {
    case Eur
    case Usd
}

這樣我們可以根據貨幣字串來獲取貨幣符號:

func symbol(input: Currency) -> String {
    switch input {
        case .Eur: return "€"
        case .Usd: return "$"
    }
}

func format(amount: Double, currency: Currency) -> String {
    let formatter = NSNumberFormatter()
    formatter.numberStyle = .CurrencyStyle
    formatter.currencySymbol = symbol(currency)
    return formatter.stringFromNumber(amount)
}

在swift不能簡單去繼承父類來擴充套件列舉型別。需要通過用協議來實現列舉的擴充套件。

protocol CurrencySymbol {
    func symbol() -> String
}

現在,我們可以讓Currency成為協議的一個例項,現在我們可以將input引數剔除,因為預設傳入self

extension Currency : CurrencySymbol {
   func symbol() -> String {
        switch self {
            case .Eur: return "€"
            case .Usd: return "$"
        }
    }
}

重寫format

func format(amount: Double, currency: CurrencySymbol) -> String {
    let formatter = NSNumberFormatter()
    formatter.numberStyle = .CurrencyStyle
    formatter.currencySymbol = currency.symbol()
    return formatter.stringFromNumber(amount)
}

現在我們可以通過讓各種各樣的資料型別來遵循這個協議的方式讓我們的程式碼具有靈活的擴充套件性,比如現在有比特幣型別:

struct Bitcoin : CurrencySymbol {
    func symbol() -> String {
        return "B⃦"
    }
}

現在通過協議而並非具體類的方式來應對各種資料型別引數值得方式可以使我們的程式碼更易於擴充套件。你可以使用便利的擴充套件搭配使用協議,你的程式碼將更具語言表達力!請根據你的實際情況決定你的忌口是開放還是封閉!

型別安全

swift還有一個優勢在於它的型別(資料)安全,正如前面說到的optionals。我們可以將型別檢查從執行時提到編譯時通過一些技巧。舉個陣列的栗子,陣列是泛型的,它可以容納同一型別的元素。不可以在字串陣列內新增整型。(當然你可以通過Either來弄出一個混合陣列。。 – -||)

又假設我們要將currency converter變成一個通用的轉換器。如果我們用Double來表示總數,這樣會導致一定的混淆。舉個例子,100.0可能意味著100美元,100千克,或者其他代表100的東東。我們要做的是讓型別系統來為我們根據物理量建立出相對應得資料型別。舉個例子,我們可以定義這樣的一個型別來描述貨幣:

struct Money {
    let amount : Double
    let currency: Currency
}

同樣,我們可以定義質量的資料結構:

struct Mass {
    let kilograms: Double
}

這樣可以增加程式碼的可讀性,假設我們有如下重量方程的介面:

func pounds(input: Double) -> Double

這樣顯然太晦澀了,我們可以這麼寫:

func pounds(input: Mass) -> Double

這樣寫的好處有二,其一是程式碼解釋性強了,第二編譯器會幫我們進行型別檢查。

不可變

另一個swift支援的特性是內建支援值不可變。在Cocoa框架裡面有很多API的引數設定為不可變額。還有成雙出現得一些類表達可變不可變(NSString vs. NSMutableString, NSArray vs. NSMutableArray)。在swift中就簡單明瞭多了:
var修飾的變數可變而let修飾的變數不可變。好處是介面更具有表達性以及涉及多執行緒操作更為容易。

總結

令作者倍感欣慰的是編譯器做了原來我們讀文件需要的活兒。然後也預測了下未來。。。略。。

相關文章