Swift 4.1 更新指北(譯)

一指厌、概述

Swift 4.1 是 Swift 4 的第一個(gè)小版本更新,主要包括一些很實(shí)用的改進(jìn)馍驯,例如,自動(dòng)合成 Equatable 和 Hashable玛痊,協(xié)議條件約束汰瘫,檢測(cè)模擬器環(huán)境等等。例子工程地址

二擂煞、自動(dòng)合成 Equatable 和 Hashable

Equatable 協(xié)議允許 Swfit 中相同類(lèi)型的兩個(gè)實(shí)例之前的比較混弥。當(dāng)我們寫(xiě) 5 == 5 的時(shí)候,Swift 之所以能夠理解是因?yàn)?Int 遵守 Equtable 協(xié)議对省,意味著它實(shí)現(xiàn)了一個(gè) == 函數(shù)描述兩個(gè) Int 之間的關(guān)系蝗拿。

然而,實(shí)現(xiàn) Equatable有點(diǎn)蛋疼蒿涎,看下面的代碼:

struct Person {
    var name: String
}

如果你有兩個(gè) Person 實(shí)例哀托,并且想要確保他們的一致性,需要比較他們的所有屬性劳秋,具體如下:

// Swift 4.0 的實(shí)現(xiàn)
struct Person: Equatable {
    var name: String

    static func ==(lhs: Person, rhs: Person) -> Bool {
        return lhs.name == rhs.name
    }
}
// Swift 4.1 實(shí)現(xiàn)
struct Person: Equatable {
    var name: String
}

上面的代碼讀起來(lái)很枯燥萤捆,寫(xiě)起來(lái)更蛋疼。比較幸運(yùn)的是俗批,Swift 4.1 能夠自動(dòng)合成 Equatable 協(xié)議中約定的方法俗或,也就是自動(dòng)生成 ==方法,方法中會(huì)比較兩個(gè)對(duì)象間的所有屬性是否相等∷晖現(xiàn)在你只需要在指定的類(lèi)型上添加 遵守Equatable 協(xié)議辛慰,其他的操作 Swift 會(huì)自動(dòng)完成。

當(dāng)然干像,如果你想你也可以自己實(shí)現(xiàn) == 帅腌。例如,如果你的類(lèi)型有一個(gè)標(biāo)記是否唯一的 id麻汰,你需要自己寫(xiě) == 來(lái)只比較這個(gè)值速客,而不是讓 Swift 來(lái)完成所有其他的工作。

Swift 4.1 也給 Hashable 協(xié)議提供了自動(dòng)合成的支持五鲫,意味著會(huì)自動(dòng)合成 hashValue 屬性溺职。Hashable 通常情況下實(shí)現(xiàn)比較蛋疼,因?yàn)樾枰祷匾粋€(gè)唯一的(或者大多數(shù)情況下唯一的)hash 值。這一點(diǎn)很重要浪耘,因?yàn)樗梢允箤?duì)象作為字典的 keys并且存儲(chǔ)在 Set中乱灵。

// Swift 4.0 實(shí)現(xiàn)
struct Person: Hashable {
    var name: String
    var hashValue: Int {
        return name.hashValue
    }

    static func ==(lhs: HashPerson, rhs: HashPerson) -> Bool {
        return lhs.name == rhs.name
    }
}
// Swift 4.1 實(shí)現(xiàn)
struct Person: Hashable {
    var name: String
}

雖然大多數(shù)情況下不用自己實(shí)現(xiàn),但是如果你想做些特別的事情的時(shí)候也可以自己實(shí)現(xiàn)七冲。

注:現(xiàn)在我們?nèi)匀恍枰岊?lèi)型遵守協(xié)議痛倚,自動(dòng)合成需要類(lèi)型的所有屬性都分別遵守了 Equatable 或者 Hashable 協(xié)議。

更多信息澜躺,請(qǐng)參照 Swift Evolution proposal SE-0185

三蝉稳、Codable Key 編解碼策略?xún)?yōu)化

在 Swift 4.0 中使用 Codable 協(xié)議一個(gè)常見(jiàn)問(wèn)題就是,JSON 中使用蛇形命名法作為 key 的名字掘鄙,而 Swift 中使用駝峰命名法颠区。Codable 不能夠理解兩種命名的差別,必須創(chuàng)建自定義的 CodingKeys 枚舉來(lái)解決這個(gè)問(wèn)題通铲。

基于上面的原因毕莱,Swift 4.1 中引入了 keyDecodingStrategy 屬性。默認(rèn)為 .useDefaultKeys 颅夺,直接映射 JSON 名字到Swift屬性朋截。可以使用 .convertFromSnakeCase 來(lái)讓 Codable 處理名字轉(zhuǎn)換吧黄。

let decoder = JSONDecoder()

do {
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    let macs = try decoder.decode([Mac].self, from: jsonData)
    print(macs)
} catch {
    print(error.localizedDescription)
}

反之部服,如果你想將遵守 Codable 協(xié)議的 struct 類(lèi)型轉(zhuǎn)成 JSONStruct 的屬性是駝峰命名的拗慨,轉(zhuǎn)成的JSON是蛇形命名的廓八,設(shè)置 keyEncodingStrategy.convertToSnakeCase

let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
let encoded = try encoder.encode(someObject)

四赵抢、協(xié)議的條件遵守

Swift 4.1 實(shí)現(xiàn)了 SE-0143 提案剧蹂,引入了條件遵守協(xié)議。具體為只有當(dāng)滿足指定條件的時(shí)候烦却,類(lèi)型才能遵守協(xié)議宠叼。

舉例來(lái)講,我們現(xiàn)在聲明一個(gè)可以用來(lái)買(mǎi)東西的協(xié)議 Purchaseable 其爵。

protocol Purchaseable {
   func buy()
}

現(xiàn)在可以定義一個(gè) Book 結(jié)構(gòu)體冒冬,遵守 Purchaseable協(xié)議,在買(mǎi)一本書(shū)的時(shí)候打印消息摩渺。

struct Book: Purchaseable {
   func buy() {
      print("You bought a book")
   }
}

這個(gè)場(chǎng)景很簡(jiǎn)單简烤,讓我們更進(jìn)一步。如果這個(gè)用戶有一個(gè)裝滿書(shū)籍的籃子摇幻,并且想把籃子里所有的書(shū)全都買(mǎi)下來(lái)横侦。我們當(dāng)然可以遍歷數(shù)組挥萌,然后調(diào)用每本書(shū)的 buy 方法。但是更好的方式是給 Array 寫(xiě)個(gè) Extension 遵守 Purchaseable 協(xié)議丈咐,然后實(shí)現(xiàn)協(xié)議 buy 方法瑞眼,調(diào)用每個(gè) Elementbuy 方法龙宏。

基于上面的原因棵逊,Swift 4.1 引入了 Conditional Conformances。如果我們嘗試拓展數(shù)組银酗,會(huì)有一定的副作用辆影。例如會(huì)給一個(gè)字符串?dāng)?shù)組添加 buy 方法,而字符串沒(méi)有 buy 方法供我們調(diào)用黍特。

Swift 4.1 可以實(shí)現(xiàn)只有當(dāng)數(shù)組中的元素是遵守 Purchaseable蛙讥,數(shù)組才能遵守 Purchaseable 協(xié)議。

extension Array: Purchaseable where Element: Purchaseable {
   func buy() {
      for item in self {
         item.buy()
      }
   }
}

如你所見(jiàn)灭衷,協(xié)議的條件遵守次慢,讓我們以更簡(jiǎn)潔的方式給拓展添加協(xié)議支持。

同樣的翔曲,協(xié)議的條件遵守也使我們的 swift 代碼更加簡(jiǎn)單和安全迫像,并且我們也不需要做些額外的工作。例如瞳遍,創(chuàng)建兩個(gè)可選字符串的數(shù)組并且比較它們是否相等闻妓。

ar left: [String?] = ["Andrew", "Lizzie", "Sophie"]
var right: [String?] = ["Charlotte", "Paul", "John"]
left == right

上面的例子看起來(lái)不那么重要,但是 Swift 4.0 上面的語(yǔ)法不能編譯掠械,String[String]Equatable由缆,但是 [String?] 不是。

Swift 4.1 中的協(xié)議的條件約束指的是只要滿足指定的條件猾蒂,就可以遵守協(xié)議均唉。上面的例子中,如果數(shù)組中的元素是遵守 Equatble 的肚菠,那么數(shù)組就是遵守 Equatable 協(xié)議浸卦。所以,上面的代碼在 Swift 4.1 上可以編譯通過(guò)案糙。

協(xié)議的條件遵守也適用于 Codable 協(xié)議限嫌,并且使代碼變得更加安全。

import Foundation

struct Person {
   var name = "Taylor"
}

var people = [Person()]
var encoder = JSONEncoder()
// try encoder.encode(people)

如果將 encoder.encode(people) 的注釋打開(kāi)时捌,在 Swift 4.1 中編譯不通過(guò)怒医,因?yàn)樵噲D encode 一個(gè)不遵守 Codable 協(xié)議的類(lèi)型。然而奢讨,這段代碼在 swift 4.0 上面是可以編譯通過(guò)的稚叹,但是因?yàn)?Person 不遵守 Codable 協(xié)議會(huì)導(dǎo)致在運(yùn)行時(shí)崩潰。

很明顯,大家都不想要運(yùn)行時(shí)崩潰扒袖。幸運(yùn)的是塞茅,Swift 4.1 使用協(xié)議的條件遵守幫我們清除了這個(gè)障礙,Optional季率、Array野瘦、DictionarySet 只有在他們的內(nèi)容遵守 Codable 協(xié)議的時(shí)候,自身才遵守協(xié)議飒泻,所以上面的代碼在 Swift 4.1 會(huì)編譯不過(guò)鞭光。

五、關(guān)聯(lián)類(lèi)型的遞歸約束

Swift 4.1 實(shí)現(xiàn)了 SE-0157 提案泞遗,增強(qiáng)了協(xié)議內(nèi)部使用關(guān)聯(lián)類(lèi)型的限制《栊恚現(xiàn)在可以給關(guān)聯(lián)類(lèi)型創(chuàng)建一個(gè)遞歸的約束,就是關(guān)聯(lián)類(lèi)型可以用自身所在協(xié)議來(lái)約束自己史辙。

我們以技術(shù)公司的管理層級(jí)來(lái)闡述這個(gè)問(wèn)題汹买,在一個(gè)公司,每一個(gè)雇員都有一個(gè)上司聊倔,每個(gè)上司必須有一個(gè)以上的下屬晦毙。我們以一個(gè) Employee 協(xié)議來(lái)表明這樣的關(guān)系:

protocol Employee {
   associatedtype Manager: Employee
   var manager: Manager? { get set }
}

盡管這是一個(gè)不言而喻的關(guān)系,但是 Swift 4.0 上這段代碼卻編譯不過(guò)方库,因?yàn)樵趨f(xié)議內(nèi)部使用了自己结序。

感謝這個(gè)新特性,我們可以模擬一個(gè)包含三種團(tuán)隊(duì)角色的技術(shù)公司纵潦,初級(jí)開(kāi)發(fā)工程師徐鹤、高級(jí)開(kāi)發(fā)工程師和董事會(huì)成員。

class BoardMember: Employee {
   var manager: BoardMember?
}

class SeniorDeveloper: Employee {
   var manager: BoardMember?
}

class JuniorDeveloper: Employee {
   var manager: SeniorDeveloper?
}

注:這邊用 Class 而不是 Struct邀层,是因?yàn)?BoardMember 里面包含一個(gè) BoardMember返敬,如果用結(jié)構(gòu)體會(huì)形成無(wú)窮大的結(jié)構(gòu)體。如果這里面有一個(gè) Class 寥院,我個(gè)人傾向于使用全部采用 Class 來(lái)保持一致劲赠。如果你想要使用結(jié)構(gòu)體,可以把 JuniorDeveloperSeniorDeveloper 設(shè)置成結(jié)構(gòu)體秸谢。

六凛澎、模塊引入檢測(cè)

Swift 4.1 實(shí)現(xiàn)了 SE-0075 提案,引入了一個(gè)新的 canImport 條件來(lái)幫助我們?cè)诰幾g期檢測(cè)一個(gè)指定的模塊能否被導(dǎo)入估蹄。

這個(gè)特性對(duì)跨平臺(tái)的代碼很有用塑煎,例如你的代碼在 macOSiOS 行為不一樣,或者你需要 Linux 的功能臭蚁。

#if canImport(SpriteKit)
   // this will be true for iOS, macOS, tvOS, and watchOS
#else
   // this will be true for other platforms, such as Linux
#endif

之前我們必須通過(guò)判斷平臺(tái)信息來(lái)處理這種情況最铁。

#if !os(Linux)
   // Matches macOS, iOS, watchOS, tvOS, and any other future platforms
#endif

#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
   // Matches only Apple platforms, but needs to be kept up to date as new platforms are added
#endif

新特性 canImport 讓我們更好的關(guān)注功能艘虎,而不是當(dāng)前編譯的平臺(tái)过蹂,避免了很多蛋疼的問(wèn)題姐呐。

七力细、模擬器環(huán)境檢測(cè)

Swift 4.1 實(shí)現(xiàn)了 SE-0190 提案,引入了 targetEnvironment 條件雀哨,幫助我們更好的區(qū)分模擬器和真機(jī)】牧拢現(xiàn)在 targetEnvironment 只有一個(gè)值 simulator,當(dāng)是模擬器設(shè)備的時(shí)候震束,返回 true怜庸。

#if targetEnvironment(simulator)
   // code for the simulator here
#else
   // code for real devices here
#endif

當(dāng)代碼用來(lái)處理類(lèi)似于從攝像頭讀取數(shù)據(jù)或者訪問(wèn)陀螺儀數(shù)據(jù)等模擬器不支持的功能的時(shí)候当犯,這個(gè)條件判斷很有用垢村。舉個(gè)例子,從攝像頭選擇照片嚎卫,如果是真機(jī)嘉栓,創(chuàng)建和配置 UIImagePickerController()方法 ,如果是模擬器拓诸,從 Bundle 中讀取一張圖片侵佃。

import UIKit

class TestViewController: UIViewController, UIImagePickerControllerDelegate {
   // a method that does some sort of image processing
   func processPhoto(_ img: UIImage) {
       // process photo here
   }

   // a method that loads a photo either using the camera or using a sample
   func takePhoto() {
      #if targetEnvironment(simulator)
         // we're building for the simulator; use the sample photo
         if let img = UIImage(named: "sample") {
            processPhoto(img)
         } else {
            fatalError("Sample image failed to load")
         }
      #else
         // we're building for a real device; take an actual photo
         let picker = UIImagePickerController()
         picker.sourceType = .camera
         vc.allowsEditing = true
         picker.delegate = self
         present(picker, animated: true)
      #endif
   }

   // this is called if the photo was taken successfully
   func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
      // hide the camera
      picker.dismiss(animated: true)

      // attempt to retrieve the photo they took
      guard let image = info[UIImagePickerControllerEditedImage] as? UIImage else {
         // that failed; bail out
         return
      }

      // we have an image, so we can process it
      processPhoto(image)
   }
}

八、FlatMap 部分場(chǎng)景更名 CompactMap

FlatMap 在 Swift 4.0 中很有用奠支,特別是在轉(zhuǎn)換集合中的對(duì)象馋辈,并且移除其中的 nil 對(duì)象的時(shí)候。Swift 提案 SE-0187 中有對(duì)這部分內(nèi)容更改的說(shuō)明倍谜,在 Swift 4.1 中 flatMap 為了語(yǔ)義更加清晰迈螟,已經(jīng)更名成 compactMap

let array = ["1", "2", "Fish"]
let numbers = array.compactMap { Int($0) }

上面例子的結(jié)果是 [1, 2]尔崔。

九答毫、展望 Swift 5

引入?yún)f(xié)議的條件遵守已經(jīng)使 Swift 團(tuán)隊(duì)提升穩(wěn)定性的同時(shí),移除了大量代碼季春,自動(dòng)合成 EquatableHashable 的支持也使我們開(kāi)發(fā)更加便捷洗搂。其他一些在開(kāi)發(fā)或者在 Review 的提案,包括 SE-0192: Non-Exhaustive Enums, SE-0194: Derived Collection of Enum Cases,和 SE-0195: Dynamic Member Lookupclick here to learn more about new Swift features coming in 2018载弄。同這些新特性一樣重要的是耘拇,蘋(píng)果計(jì)劃在今年實(shí)現(xiàn) Swift 的 ABI 穩(wěn)定,期待??????宇攻。

十惫叛、感謝原作者 Paul Hudson原文地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末尺碰,一起剝皮案震驚了整個(gè)濱河市挣棕,隨后出現(xiàn)的幾起案子译隘,更是在濱河造成了極大的恐慌,老刑警劉巖洛心,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件固耘,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡词身,警方通過(guò)查閱死者的電腦和手機(jī)厅目,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)法严,“玉大人损敷,你說(shuō)我怎么就攤上這事∩钇。” “怎么了拗馒?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)溯街。 經(jīng)常有香客問(wèn)我诱桂,道長(zhǎng),這世上最難降的妖魔是什么呈昔? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任挥等,我火速辦了婚禮,結(jié)果婚禮上堤尾,老公的妹妹穿的比我還像新娘肝劲。我一直安慰自己,他們只是感情好郭宝,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布辞槐。 她就那樣靜靜地躺著,像睡著了一般剩蟀。 火紅的嫁衣襯著肌膚如雪催蝗。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天育特,我揣著相機(jī)與錄音丙号,去河邊找鬼。 笑死缰冤,一個(gè)胖子當(dāng)著我的面吹牛犬缨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播棉浸,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼怀薛,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了迷郑?” 一聲冷哼從身側(cè)響起枝恋,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤创倔,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后焚碌,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體畦攘,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年十电,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了知押。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鹃骂,死狀恐怖台盯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情畏线,我是刑警寧澤静盅,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站象踊,受9級(jí)特大地震影響温亲,放射性物質(zhì)發(fā)生泄漏棚壁。R本人自食惡果不足惜杯矩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望袖外。 院中可真熱鬧史隆,春花似錦、人聲如沸曼验。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鬓照。三九已至熔酷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間豺裆,已是汗流浹背拒秘。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留臭猜,地道東北人躺酒。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像蔑歌,于是被迫代替她去往敵國(guó)和親羹应。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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