SwiftAPI設(shè)計之使用@autoclosure

原文鏈接:Using @autoclosure when designing Swift APIs

Swift的@autoclosure屬性能讓你定義一個自動被閉包的參數(shù)。它的主要作用是推遲表達(dá)式(可能代價高昂)的執(zhí)行,從而避免在傳遞參數(shù)時就直接執(zhí)行。

assert

在Swift標(biāo)準(zhǔn)庫中的assert函數(shù)就使用了@autoclosure屬性。我們都知道斷言只會在debug模式下被觸發(fā),因此在release模式下是沒有必要執(zhí)行斷言表達(dá)式的。assert定義如下

func assert(_ expression: @autoclosure () -> Bool, 
            _ message: @autoclosure () -> String) {
    guard isDebug else {
        return
    }

    // 在assert內(nèi)部,我們可以把表達(dá)式當(dāng)做一個正常的閉包來使用
    if !expression() {
        assertFailure(message())
    }
}

上面是我簡單模擬assert的實(shí)現(xiàn),真正的實(shí)現(xiàn)在這里

@autoclosure的好處在于它對調(diào)用方?jīng)]有任何影響。如果assert使用正常的閉包來定義的話,那么你就必須這么使用它:

assert({ someCondition() }, { "Hey, it failed!" })

但是現(xiàn)在,你可以像調(diào)用非閉包參數(shù)一樣調(diào)用它:

assert(someCondition(), "Hey it failed!")

接下來,讓我們來看看如何在自己的代碼中使用@autoclosure屬性,來使我們的API更友好。

內(nèi)聯(lián)賦值

@autoclosure可以在函數(shù)調(diào)用中內(nèi)聯(lián)表達(dá)式。我們能利用它做一些事情,比如傳遞賦值表達(dá)式作為參數(shù)。我們看看下面這個可能有用的例子。

UIView.animate(withDuration: 0.25) {
    view.frame.origin.y = 100
}

使用@autoclosure,我們可以編寫一個自動創(chuàng)建動畫閉包并執(zhí)行它的動畫函數(shù),如下所示:

func animate(_ animation: @autoclosure(escaping) () -> (),
             duration: TimeInterval = 0.25) {
    UIView.animate(withDuration: duration, animations: animation)
}

現(xiàn)在我們可以使用簡單的函數(shù)調(diào)用來執(zhí)行動畫,而不需要額外的{}語法:

animate(view.frame.origin.y = 100)

使用@autoclosure,我們可以真正減少動畫代碼的冗長度,而不會犧牲可讀性或變現(xiàn)力??。

使用表達(dá)式傳遞錯誤

我發(fā)現(xiàn)@autoclosure的另一個非常有用的情況:編寫處理錯誤的代碼。比如,假設(shè)我們要在Optional上添加一個擴(kuò)展,使我們能夠在解包它出錯時拋出異常。這樣我們可以要求Optional是非nil,否則將拋出異常。如下所示:

extension Optional {
    func unwrapOrThrow(_ errorExpression: @autoclosure () -> Error) throws -> Wrapped {
        guard let value = self else {
            throw errorExpression()
        }
        return value
    }
}

類似于assert的實(shí)現(xiàn),我們只會在需要的時候執(zhí)行表達(dá)式,而不是每次嘗試解包時都要執(zhí)行?,F(xiàn)在我們可以像這樣使用我們的unwrapOrThrow

let name = try argument(at: 1).unwrapOrThrow(ArgumentError.missingName)

使用默認(rèn)值做類型推斷

我發(fā)現(xiàn)的最后的使用場景是從dictionary,database或者UserDefaults中提取可選值。

通常,當(dāng)從一個沒有指明特定類型的字典中提取一個值并提供一個默認(rèn)值時,你必須這么寫:

let coins = (dictionary["numberOfCoins"] as? Int) ?? 100

這種方式難以閱讀,并且有很多復(fù)雜的語法糖。使用@autoclosure,我們可以定義一個API,來讓我們想下面這樣實(shí)現(xiàn)同樣的功能:

let coins = dictionary.value(forKey: "numberOfCoins", defaultValue: 100)

從上面,我們可以看到默認(rèn)值可以拿來做類型推斷,而不需要指定類型來完成類型轉(zhuǎn)換。很簡潔??

讓我們來看看如何編寫這個API:

extension Dictionary where Value == Any {
    func value<T>(forKey key: Key, defaultValue: @autoclosure () -> T) -> T {
        guard let value = self[key] as? T else {
            return defaultValue()
        }

        return value
    }
}

再次強(qiáng)調(diào),我們使用@autoclosure來避免每次調(diào)用方法是都執(zhí)行默認(rèn)值表達(dá)式。

結(jié)論

減少冗長總是需要仔細(xì)考慮的事情。 我們的目標(biāo)應(yīng)該始終是編寫富有表現(xiàn)力,易于閱讀的代碼,所以我們需要確保在設(shè)計低冗余性API時不會在使用時丟掉重要信息。

我認(rèn)為在適當(dāng)?shù)那闆r下使用@autoclosure是一個很好的工具。用表達(dá)式代替數(shù)值,使我們能夠減少冗長和多余,同時也可能獲得更好的性能。

感謝閱讀! ??

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,569評論 19 139
  • 86.復(fù)合 Cases 共享相同代碼塊的多個switch 分支 分支可以合并, 寫在分支后用逗號分開。如果任何模式...
    無灃閱讀 1,556評論 1 5
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,063評論 25 709
  • 本章將會介紹 閉包表達(dá)式尾隨閉包值捕獲閉包是引用類型逃逸閉包自動閉包枚舉語法使用Switch語句匹配枚舉值關(guān)聯(lián)值原...
    寒橋閱讀 1,629評論 0 3
  • 今天睡午覺的時候,忽然,一陣狂風(fēng)吹來,原本安靜的教室立刻熱鬧起來。我被驚醒了,但是我沒有睜眼,細(xì)細(xì)分辨著聲音,...
    遇見蝴蝶閱讀 298評論 0 2

友情鏈接更多精彩內(nèi)容