Swift 中最棒的新特性

更多優(yōu)秀譯文請(qǐng)關(guān)注我們的微信公眾號(hào):learnSwift

原文鏈接:Friday Q&A 2015-06-19: The Best of What's New in Swift
原文日期:2015/06/19

譯者:Yake
校對(duì):numbbbbb
定稿:shanksyang

蘋果公司在今年的 WWDC 大會(huì)上發(fā)布了 Swift 2 以及相關(guān)的新特性扣孟,相比之下其他的內(nèi)容就無聊多了枣抱。除了宣布 Swift 將會(huì)開源并且這門語言由蘋果獨(dú)立開發(fā)完成之外炭剪,Swift 2 還包含很多新的特性创倔,這將大幅改善這門語言。今天我將介紹最重要的幾個(gè)新特性琢歇。

函數(shù)指針

這是目前為止我最喜歡的 Swift 新特性。這是一個(gè)比較小但是非常重要的特性梦鉴,因?yàn)樗钛a(bǔ)了 Swift 橋接 C 語言的最后一個(gè)漏洞李茫。

之前,Swift 將 C 語言中的函數(shù)指針類型作為不透明類型肥橙。如果拿到一個(gè)函數(shù)指針魄宏,你可以持有它。如果函數(shù)指針是一個(gè)參數(shù)存筏,你可以傳遞它宠互。但是你不能調(diào)用函數(shù)指針味榛,更重要的是不能創(chuàng)建一個(gè)指向你的 Swift 代碼的函數(shù)指針。你可以獲取一個(gè)CFunctionPointer類型的指針但是你幾乎什么都做不了予跌。與這些 API 交互的最明智的方法就是把它們封裝在 C 或者 OC 的 API 中搏色。

Swift 2 中CFunctionPointer的神秘世界即將結(jié)束,Swift 中的函數(shù)類型已經(jīng)有了許多變種券册。任何 Swift 函數(shù)類型都可以選擇性地通過@convention標(biāo)示符來進(jìn)行注解频轿,以此說明函數(shù)類型。默認(rèn)的標(biāo)注是 swift烁焙,表示這是一個(gè)正常的 Swift 函數(shù)航邢。標(biāo)注為block則說明這是 OC 中的block類型。這些一直都是自動(dòng)橋接的骄蝇,但是現(xiàn)在書寫方式更加明確了膳殷。最后,c標(biāo)注表明這是一個(gè)C語言函數(shù)指針九火。通過@convention(c)標(biāo)注的函數(shù)類型在多數(shù)情況下表現(xiàn)正常秽之,所以你可以像往常那樣調(diào)用并傳遞他們。

上面之所以說“多數(shù)情況下”是因?yàn)?C 語言函數(shù)類型有很大的限制吃既,而這必然會(huì)在 Swift 中體現(xiàn)出來考榨。特別的,C 語言函數(shù)指針在編碼中是純粹的指針鹦倚,沒有相關(guān)聯(lián)的數(shù)據(jù)河质。而 Swift 函數(shù)經(jīng)常會(huì)有內(nèi)含的相關(guān)數(shù)據(jù),例如對(duì)象中的方法震叙,或者是從某段封閉的代碼中捕獲值的閉包掀鹅。由于這種不匹配,只有全局 Swift 函數(shù)媒楼、嵌套函數(shù)或者沒有捕獲值的匿名函數(shù)可以被作為@convention(c)標(biāo)記類型的參數(shù)傳遞乐尊。

下面是這種新特性在實(shí)際中的例子:

atexit({ print("Goodbye!") })

當(dāng)程序退出的時(shí)候會(huì)打印“Goodbye!”

下面這段代碼將不會(huì)起作用,這就體現(xiàn)了 C 函數(shù)指針的限制性(除非被放在全局范圍內(nèi)):

let x = 42
atexit({ print("The answer is \(x)") })
atexit({ print("Goodbye!") })

編譯器解釋:C 語言指針不能由捕獲上下文內(nèi)容的閉包形成划址。

這種限制很正常扔嵌,但也是我們?cè)诟?C 語言打交道時(shí)需要經(jīng)常處理的問題。
盡管如此夺颤,這依然很有用痢缎,我們可以借此使用 C 語言 API 中的一些類。

協(xié)議擴(kuò)展

這是目前為止我最喜歡的 Swift 新特性世澜。協(xié)議擴(kuò)展允許協(xié)議包含方法的實(shí)現(xiàn)独旷,而不僅僅是方法的聲明。這是我多年以來都希望在 OC 中出現(xiàn)的特性,我很開心地看到它出現(xiàn)在了一門新的語言中嵌洼。

之前案疲,在 OC 和 Swift 中,協(xié)議只包含方法的聲明麻养。這純粹是定義接口褐啡,即列舉了一些遵守協(xié)議的類需要實(shí)現(xiàn)的一些方法。而在 Swift 2 中回溺,協(xié)議不僅包括方法的聲明也包括方法實(shí)現(xiàn)春贸。

通常會(huì)有一些方法適用于所有遵守了某個(gè)特定接口的類。例如遗遵,所有的集合都支持map操作萍恕,可以通過map來創(chuàng)建一個(gè)新集合。在舊版的協(xié)議中车要,有兩種方式可以使集合具有這個(gè)功能:你可以把方法放在協(xié)議中允粤,要求每一個(gè)遵守協(xié)議的類型都實(shí)現(xiàn)這些方法,也可以寫一個(gè)全局函數(shù)翼岁,通過這個(gè)函數(shù)來處理每個(gè)遵守協(xié)議的類型类垫。

Cocoa 大多數(shù)情況下使用前一種解決方案±牌拢可變的 Cocoa 集合類型也有相似的功能悉患,雖然他們不是任何正式協(xié)議的一部分,如enumerateObjectsUsingBlock:榆俺,每一個(gè)集合類型的對(duì)象都會(huì)單獨(dú)實(shí)現(xiàn)這個(gè)方法售躁。

Swift 在以前采用后一種解決方案。全局函數(shù)如map作用于所有遵守CollectionType協(xié)議的類型茴晋。這很容易實(shí)現(xiàn)代碼復(fù)用陪捷,但是語法很蹩腳,并且也不能根據(jù)某個(gè)特定的類型進(jìn)行定制诺擅。

有了協(xié)議擴(kuò)展市袖,就有了第三種選擇:可以在CollectionType協(xié)議的擴(kuò)展中實(shí)現(xiàn)superior.map方法。所有遵守CollectionType協(xié)議的類就自動(dòng)獲得了map方法的實(shí)現(xiàn)烁涌。

下面簡(jiǎn)單實(shí)現(xiàn)了map函數(shù):

extension CollectionType {
    func myMap<U>(f: Self.Generator.Element -> U) -> [U] {
        var result: [U] = []
        for elt in self {
            result.append(f(elt))
        }
        return result
    }
}

[1, 2, 3, 4].myMap({ $0 * 2 })
// This produces [2, 4, 6, 8]

之前在數(shù)組中可以通過擴(kuò)展實(shí)現(xiàn)苍碟,但是只在Array類型中可用。有了協(xié)議擴(kuò)展烹玉,就可以毫不費(fèi)力地使其同樣適用于任何遵守CollectionType協(xié)議的類驰怎,比如SetArraySlice

對(duì)于Swift協(xié)議擴(kuò)展來說二打,一個(gè)特別有趣的特性是它可以被賦予類型約束。例如掂榔,你可能想實(shí)現(xiàn)一個(gè)max屬性继效。但是max屬性在概念上并不適用于所有的集合症杏,只有集合中的對(duì)象有某種順序時(shí)max才起作用。那不是問題瑞信,只要給擴(kuò)展加一條限制即集合中的元素必須是可比較的(Comparable)

extension CollectionType where Self.Generator.Element: Comparable {
    var max: Self.Generator.Element {
        var best = self[self.startIndex]
        for elt in self {
            if elt > best {
                best = elt
            }
        }
        return best
    }
}

Set([5, 4, 3, 2, 1]).max
// This produces 5

[NSObject(), NSObject()].max
// This produces an error, as NSObject is not Comparable

協(xié)議擴(kuò)展有一個(gè)很小但是也很重要的特質(zhì)厉颤,這個(gè)特性決定了協(xié)議擴(kuò)展方法是否可以被動(dòng)態(tài)調(diào)度。

協(xié)議擴(kuò)展中實(shí)現(xiàn)的方法可能在協(xié)議本身中聲明凡简,也可能只存在于協(xié)議擴(kuò)展中逼友。只存在于協(xié)議擴(kuò)展中的方法不能被動(dòng)態(tài)調(diào)度且不能被重載。而同時(shí)也在協(xié)議本身中聲明的方法可以被動(dòng)態(tài)調(diào)度且可以被重載秤涩。這有點(diǎn)難解釋帜乞,下面是一個(gè)例子:

protocol P {
    func a()
}

extension P {
    func a() {
        print("default implementation of A")
    }

    func b() {
        print("default implementation of B")
    }
}

struct S: P {
    func a() {
        print("specialized implementation of A")
    }

    func b() {
        print("specialized implementation of B")
    }
}

let p: P = S()
p.a()
p.b()

打印結(jié)果是"specialized implementation of A"后面跟著"default implementation of B.”。雖然Struct包含了b的實(shí)現(xiàn)筐眷,但是它沒有能夠覆蓋協(xié)議的b方法黎烈,因?yàn)閰f(xié)議沒有包含方法b的聲明。本質(zhì)區(qū)別在于匀谣,協(xié)議中聲明的方法是有默認(rèn)實(shí)現(xiàn)的照棋,而協(xié)議擴(kuò)展中的方法實(shí)現(xiàn)是依附于協(xié)議的。

我相信協(xié)議擴(kuò)展是蘋果對(duì)于可選協(xié)議方法這個(gè)問題的答案武翎。純粹的 Swift 協(xié)議不包含可選協(xié)議方法烈炭。在 OC 中我們已經(jīng)習(xí)慣了用類似代理的方式來實(shí)現(xiàn)可選協(xié)議方法:

@protocol MyClassDelegate
@optional

- (BOOL)shouldDoThingOne;
- (BOOL)shouldDoThingTwo

@end

協(xié)議的遵守者在調(diào)用這些方法時(shí)將會(huì)檢查respondsToSelector:,純粹的 Swift 沒有對(duì)應(yīng)功能:

 protocol MyClassDelegate {
    func shouldDoThingOne() -> Bool
    func shouldDoThingTwo() -> Bool
}

之前宝恶,所有遵守協(xié)議的類型都被要求實(shí)現(xiàn)這些方法符隙。Cocoa 認(rèn)為代理已經(jīng)可以實(shí)現(xiàn) Swift 2 中的協(xié)議擴(kuò)展,它與 Objective-C 中的@optional基本相同卑惜,但是不要求運(yùn)行時(shí)檢查膏执。

錯(cuò)誤處理

這是目前為止我最喜歡的 Swift 新特性(原諒博主愛的太多)。Swift 剛出現(xiàn)時(shí)不支持異常處理露久,許多人對(duì)此很絕望更米。不過這種絕望毫無意義,因?yàn)?OC 也沒有真正地支持異常處理毫痕。雖然有語法征峦、編譯器、運(yùn)行時(shí)機(jī)制來處理異常消请,但是通過代碼來處理異常是很麻煩的栏笆,許多人都不處理。當(dāng)使用 ARC 時(shí)就更加證實(shí)了上述觀點(diǎn)臊泰,因?yàn)?ARC 中默認(rèn)形成的代碼不是異常安全(exception safe)的蛉加。

考慮到這種情況,OC 實(shí)際上只用異常來標(biāo)識(shí)編程錯(cuò)誤。有不少這樣的異常针饥,不過 Cocoa 中一般只使用異常來標(biāo)識(shí)斷言失敵С椤(assertion failures)。第三方的代碼通常也是這樣丁眼。這有些奇怪筷凤,這也就是說你可以捕獲異常然后繼續(xù)運(yùn)行,但是在斷言失敗之后還這么做就不是什么好主意了苞七。

由于實(shí)際在 OC 中異常并不能真的被用于標(biāo)識(shí)可恢復(fù)性錯(cuò)誤藐守,Cocoa 采用**NSError **慣例,這樣一來所有可能產(chǎn)生錯(cuò)誤的方法都有了一個(gè)額外的NSError ****類型的參數(shù)蹂风,用那個(gè)參數(shù)來返回錯(cuò)誤信息卢厂。這的確有用,但是NSError **接口設(shè)計(jì)得不好硫眨,用它反而會(huì)讓你的代碼變得更糟足淆。

你也可以忽略錯(cuò)誤,因?yàn)閼T例中這些參數(shù)也可以接收NULL礁阁,那就意味著“我不關(guān)心錯(cuò)誤巧号,不用把它傳給我”。但是我想告訴你姥闭,我見過很多次我的同事因?yàn)橐欢未a錯(cuò)誤急的抓耳撓腮丹鸿,因?yàn)樗麄兘oerror參數(shù)傳了NULL。如果你愿意傾聽的話棚品,程序會(huì)告訴你到底哪里出了問題靠欢。

Swift 2 中引進(jìn)了一種錯(cuò)誤處理方式,這種方式試圖找到以上兩種技術(shù)的平衡點(diǎn)铜跑。在語句構(gòu)成上门怪,它效法異常。在語義上锅纺,它更像是NSError **掷空。最終的樣子看起來有點(diǎn)奇怪,但是效果相當(dāng)不錯(cuò)囤锉。

讓我們來看一個(gè)例子坦弟。將一個(gè)文件夾寫入磁盤是一個(gè)可能會(huì)失敗的普通操作。在 Cocoa 中官地,它看起來是這樣的:

NSError *error;
BOOL success = [data writeToFile: path options: options error: &error];
if(!success) {
    // respond to the error somehow
    return;
}
// proceed normally

如果 Cocoa 使用異常來標(biāo)記這類錯(cuò)誤酿傍,代碼看起來是這樣的:

@try {
    [data writeToFile: path options: options];
    // proceed normally
} @catch(NSError *error) {
    // respond to the error somehow
}

在 Swift 2 中,你會(huì)這么寫:

do {
    try data.writeToFile(path, options: options)
    // proceed normally
} catch {
    // respond to the error somehow
}

代碼表面上看與異常相似驱入,但是 Swift 中的方法與異常是有很大不同的赤炒。

Swift 的錯(cuò)誤是由編譯器檢查的氯析。這與一些語言(比如 Java)很像,在 Java 中拋出異常的可能性是方法類型簽名的一部分可霎。在 OC 以及其他一些語言中一切都可以潛在地拋出異常魄鸦,而 Swift 在本質(zhì)上與他們不同宴杀。

如果程序中的異常不能被完全監(jiān)測(cè)到癣朗,那就意外著你要么在每一段代碼中加入異常處理,要么你就需要去查閱資料(文檔旺罢、源代碼)確認(rèn)哪些方法能夠拋出異常哪些不能旷余。在 Swift 的方法中,如果你忘了writeToFile拋出的異常扁达,編譯器就會(huì)告訴你正卧。而 OC 的方法中,你的代碼會(huì)編譯跪解、運(yùn)行并且正常工作炉旷,直到有一天寫入操作失敗,你會(huì)突然發(fā)現(xiàn)自己就像倫敦東區(qū)的皮鞋匠叉讥,在維多利亞人閃閃發(fā)亮的鞋子中思考著“未定義行為”的意義窘行。

我所了解的(目前為止)Swift 錯(cuò)誤處理中比較特別的一點(diǎn)是 ,每一句可能拋出異常的代碼前面都要有 try 關(guān)鍵字图仓。而 Java 樣式的異常檢查罐盔,唯一的要求就是,要么把拋出異常的方法放在一個(gè)能夠返回一個(gè)合適的異常類型的方法中救崔,要么放在一個(gè) try 代碼塊中惶看。為了列舉這個(gè)不同點(diǎn),請(qǐng)看這兩段假設(shè)的代碼:

// Java style
try {
    String name = textField.value();
    Data nameBytes = name.getBytes("UTF-8");
    nameBytes.writeToFile(path);
    proceedWithName(name);
} catch(IOException exception) {
    ...
}

// Swift style
do {
    let name = textField.value
    let nameBytes = name.dataUsingEncoding(NSUTF8StringEncoding)!
    try nameBytes.writeToFile(path, options: [])
    proceedWithName(name)
} catch {
    ...
}

快看六孵,第一個(gè)版本拋出了哪個(gè)調(diào)用纬黎?除非你查閱try代碼段中每句調(diào)用的接口否則你無法知道。現(xiàn)在劫窒,第二個(gè)版本哪個(gè)調(diào)用會(huì)被拋出本今?那很簡(jiǎn)單:writeToFile這句。

你可能會(huì)說只從那些調(diào)用上看就很明顯拋出了什么烛亦。但是或許proceedWithName也可以產(chǎn)生錯(cuò)誤并且拋出異常诈泼。對(duì)比一下:

// Java style
try {
    String name = textField.value();
    Data nameBytes = name.getBytes("UTF-8");
    nameBytes.writeToFile(path);
    proceedWithName(name);
} catch(IOException exception) {
    ...
}

// Swift style
do {
    let name = textField.value
    let nameBytes = name.dataUsingEncoding(NSUTF8StringEncoding)!
    try nameBytes.writeToFile(path, options: [])
    try proceedWithName(name)
} catch {
    ...
}

Java 版本的沒有變化,而 Swift 版則顯示現(xiàn)在有兩個(gè)調(diào)用可能會(huì)失敗煤禽。

在 Java 中铐达,你通常會(huì)盡量減少 try 代碼塊中的代碼,就是為了能夠明顯辨別哪句調(diào)用能拋出異常而哪些只是附帶的代碼檬果。在 Swift 中瓮孙,你不需要擔(dān)心這個(gè)唐断,因?yàn)槊恳痪鋻伋霎惓5拇a都被明確做了標(biāo)記。

當(dāng)整個(gè)方法都被標(biāo)記為throws時(shí)那就顯得意義重大了杭抠。Java 類型的異常檢查脸甘,一旦你將某個(gè)方法聲明為throws IOException,那么方法中的一切都可以拋出那個(gè)類型偏灿。在 Swift 中丹诀,一個(gè)被標(biāo)記為throws的方法里面每一句可能拋出異常的調(diào)用前面都需要都try關(guān)鍵字進(jìn)行標(biāo)注,所有異常的拋出還是很明顯翁垂。

另外一個(gè)與 Java 不同的地方是铆遭,Swift 有一個(gè)內(nèi)嵌的以try!形式存在的“不失敗”機(jī)制。有時(shí)一個(gè)方法只會(huì)在某種情況下調(diào)用失敗沿猜,并且你知道你所使用的那種情況下它是不會(huì)失敗的枚荣。上述getBytes調(diào)用就是 Java 中的一個(gè)很好的例子:它拋出UnsupportedEncodingException但是它能保證傳入“UTF-8”時(shí)一定不會(huì)拋出異常。即使你知道這不會(huì)失敗但調(diào)用時(shí)需要用try來解包啼肩。在 Swift 中橄妆,你可以使用try!來完成這些,既清楚又簡(jiǎn)短祈坠。這與“!”后綴語法配合得很好害碾,!后綴用來解包你知道肯定不會(huì)為nil的可選類型,就像上面的dataUsingEncoding颁虐,類似的還有as!操作符來轉(zhuǎn)換類型并且你已知這個(gè)操作肯定會(huì)成功蛮原。

和 Java 相比,Swift 中的錯(cuò)誤被檢查時(shí)另绩,只能檢查一個(gè)錯(cuò)誤能夠拋出異常儒陨。但 Java 中的方法用throws標(biāo)注時(shí)就指明了能被拋出的類型。調(diào)用者知道它只需要檢查這些類型笋籽,而編譯器對(duì)應(yīng)地將一切進(jìn)行類型檢查蹦漠。Swift 中用throws標(biāo)注時(shí)沒有類型信息。這種類型信息在 Java 中確實(shí)有用车海,但是嵌套throws標(biāo)注的語句時(shí)就有局限性笛园。這在 Swift 中會(huì)導(dǎo)致什么結(jié)果呢?應(yīng)該會(huì)很有趣侍芝。

Guard語句

這是目前為止我最喜歡的 Swift 特性(他到底最喜歡哪個(gè)啊……)研铆。它很小也很簡(jiǎn)單你甚至?xí)X得它是多余的。但是它會(huì)讓代碼的讀寫性變得更好州叠。

Guard語句實(shí)際上是一個(gè)反轉(zhuǎn)的if語句棵红。在if語句中你這么寫:

if condition {
    // true branch
} else {
    // false branch
}

有了guard,為真的那個(gè)分支從上面跑到了為假的那個(gè)分支的下面:

guard condition else {
    // false branch
}
// true branch

注意為假的分支中的代碼會(huì)在那個(gè)范圍內(nèi)以某種方式終止執(zhí)行咧栗,比如返回一個(gè)值或者是拋出錯(cuò)誤逆甜。你就能被保證為真的分支中的代碼只在條件為真的情況下執(zhí)行虱肄。

這使得guard成為一種能夠排除錯(cuò)誤條件的很自然的方式,有了這種方式交煞,在多重if嵌套語句中就不需要一直走到“金字塔頂“咏窿,并且也不需要將條件反轉(zhuǎn)。下面是一個(gè)典型的金字塔:

 let fd1 = open(...)
if fd1 >= 0 {
    let fd2 = open(...)
    if fd2 >= 0 {
        // use fd1 and fd2 here
        close(fd2)
    } else {
        // handle fd2 error
    }
    close(fd1)
} else {
    // handle fd1 error
}

這實(shí)在是太丑了素征,并且隨著你把它們一層層堆起來它會(huì)變得更糟集嵌。代碼一直在縮進(jìn)并且錯(cuò)誤處理代碼被堆到了老遠(yuǎn)的地方。為比避免這種情況發(fā)生我們可以反轉(zhuǎn)條件:

let fd1 = open(...)
if fd1 == -1 {
    // handle fd1 error
    return
}

let fd2 = open(...)
if fd2 == -1 {
    // handle fd2 error
    close(fd1)
    return
}

// use fd1 and fd2 here
close(fd1)
close(fd2)

這看起來好多了稚茅,但是煩人的是纸淮,條件現(xiàn)在被反轉(zhuǎn)為檢查錯(cuò)誤情況而不是正確情況了。更糟的是如果你忘了return語句亚享,編譯器就不管了,你的代碼會(huì)開開心心地在錯(cuò)誤情況執(zhí)行完之后繼續(xù)執(zhí)行绘面。guard解決了所有這些問題:

let fd1 = open(...)
guard fd1 >= 0 else {
    // handle fd1 error
    return
}

let fd2 = open(...)
guard fd2 >= 0 else {
    // handle fd2 error
    close(fd1)
    return
}

// use fd1 and fd2 here
close(fd1)
close(fd2)

這就更好了欺税!這看起來更清晰了,并且從編譯器那里獲得了更多的幫助揭璃。但是這并沒有什么特別的啊晚凿,那為什么這是我最喜歡的呢?那是因?yàn)槭葩桑?code>if語句一樣歼秽,guard語句也可以包含變量聲明并且檢查是否為nil。但guard語句又不像if語句那樣情组,聲明的變量不僅僅是在guard語句范圍內(nèi)可用燥筷。為了幫助大家理解,我們先來看上面例子的一個(gè)可選類型版本院崇,首先是那個(gè)金字塔:

if let file1 = Open(...) {
    if let file2 = Open(...) {
        // use file1 and file2
        file2.close()
    } else {
        // handle file2 error
    }
    file1.close()
} else {
    // handle file1 error
}

我們像之前那樣反轉(zhuǎn)條件:

if !let file1 =

哦肆氓!你不能反轉(zhuǎn)let語句,我們?cè)僭囋嚕?/p>

let file1 = Open(...)
if file1 == nil {
    // handle file1 error
    return
}

let file2 = Open(...)
if file2 == nil {
    // handle file2 error
    file1.close()

哦底瓣!可選類型沒有解包谢揪。我們必須單獨(dú)處理。再試試:

let file1Optional = Open(...)
if file1Optional == nil {
    // handle file1 error
    return
}
let file1 = file1Optional!

let file2Optional = Open(...)
if file2Optional == nil {
    // handle file2 error
    file1.close()
    return
}
let file2 = file2Optional!

// use file1 and file2 here
file1.close()
file2.close()

好了捐凭。但是一團(tuán)亂拨扶。guard會(huì)讓它變得更好:

guard let file1 = Open(...) else {
    // handle file1 error
    return
}
guard let file2 = Open(...) else {
    // handle file2 error
    file1.close()
    return
}

// use file1 and file2 here
file1.close()
file2.close()

更好了!唯一不好的是file1.close()代碼重復(fù)了茁肠,并且清除代碼確實(shí)離初始化的代碼太遠(yuǎn)了患民,這……

Defer語句

這是目前為止我最愛的 Swift 新特性(又見最愛……)。defer語句與很多其他語言中的finally語句很像官套,不過它不需要與try語句綁定在一起酒奶,你可以把它放在任何你想放的位置蚁孔。如果你寫了defer{…},那么那個(gè)代碼塊中的代碼就會(huì)在控制離開當(dāng)前函數(shù)的范圍時(shí)執(zhí)行惋嚎,無論函數(shù)最后是運(yùn)行到了結(jié)尾杠氢,還是遇到了return語句,或者是拋出了錯(cuò)誤另伍。

這就可以讓你把清除代碼放在要清除的那些東西的后面鼻百,而不是在最后。例如:

let tmpMemory = malloc(...)
defer { free(tmpMemory) }

// use tmpMemory here

它與guard語句搭配得很好摆尝,這樣一來温艇,創(chuàng)建、錯(cuò)誤處理以及清除都可以共存了堕汞。下面是上述例子使用了defer語句之后:

guard let file1 = Open(...) else {
    // handle file1 error
    return
}
defer { file1.close() }

guard let file2 = Open(...) else {
    // handle file2 error
    return
}
defer { file2.close() }

// use file1 and file2 here
// no need for cleanup at the end, it's already done

注意file1defer語句不僅處理了正常情況還處理了file2失敗時(shí)的情況勺爱。這個(gè)就清除了之前例子中的重復(fù)代碼,并且?guī)椭覀冊(cè)谌魏畏种е卸疾煌饲宄僮餮都臁S捎阱e(cuò)誤常常不能被測(cè)試琐鲁,因此產(chǎn)生錯(cuò)誤時(shí)不能正常執(zhí)行清理操作是一個(gè)很常見的問題,而defer可以保證那種情況不會(huì)發(fā)生人灼。

結(jié)論

這是目前為止我最喜歡的關(guān)于 Swift 新特性的文章围段。看起來 Swift 2 在 Swift 的基礎(chǔ)上有了很大的提升投放,解決了許多不足之處并添加了很多重大改進(jìn)奈泪。

今天就這樣吧,歡迎下次再來學(xué)習(xí)一些新的并且激動(dòng)人心的東西灸芳。Friday Q&A 是在讀者的建議中產(chǎn)生的涝桅,所以如果你想看到哪些內(nèi)容,請(qǐng)發(fā)給耗绿。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末苹支,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子误阻,更是在濱河造成了極大的恐慌债蜜,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件究反,死亡現(xiàn)場(chǎng)離奇詭異寻定,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)精耐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門狼速,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人卦停,你說我怎么就攤上這事向胡∧张睿” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵僵芹,是天一觀的道長(zhǎng)处硬。 經(jīng)常有香客問我,道長(zhǎng)拇派,這世上最難降的妖魔是什么荷辕? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮件豌,結(jié)果婚禮上疮方,老公的妹妹穿的比我還像新娘。我一直安慰自己茧彤,他們只是感情好骡显,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著棘街,像睡著了一般蟆盐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上遭殉,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音博助,去河邊找鬼险污。 笑死,一個(gè)胖子當(dāng)著我的面吹牛富岳,可吹牛的內(nèi)容都是我干的蛔糯。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼窖式,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蚁飒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起萝喘,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤淮逻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后阁簸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體爬早,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年启妹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了筛严。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡饶米,死狀恐怖桨啃,靈堂內(nèi)的尸體忽然破棺而出车胡,到底是詐尸還是另有隱情,我是刑警寧澤照瘾,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布匈棘,位于F島的核電站,受9級(jí)特大地震影響网杆,放射性物質(zhì)發(fā)生泄漏羹饰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一碳却、第九天 我趴在偏房一處隱蔽的房頂上張望队秩。 院中可真熱鬧,春花似錦昼浦、人聲如沸馍资。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鸟蟹。三九已至,卻和暖如春使兔,著一層夾襖步出監(jiān)牢的瞬間建钥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工虐沥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留熊经,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓欲险,卻偏偏與公主長(zhǎng)得像镐依,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子天试,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

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