Swift 4.2 更新指北(譯)

一、概述

好消息是尔,Swift 4.2 在 Xcode 10 beta 版上可以使用了瞻鹏,在 Swift 4.1 的基礎(chǔ)上更新了很多語(yǔ)言特性,為 Swift 5 中 ABI 穩(wěn)定做好準(zhǔn)備并思。

這篇文章包含了 Swift 4.2 中的重大的改變。因?yàn)?Swift 4.2 需要 Xcode 10语稠,所以請(qǐng)下載安裝最新的 Xcode 測(cè)試版本宋彼。

二、準(zhǔn)備

Swift 4.2 和 Swift 4.1 源碼兼容仙畦,但是和其他發(fā)布版本的二進(jìn)制不兼容输涕。Swift 4.2 是 Swift 5 實(shí)現(xiàn) ABI 穩(wěn)定(不同的 Swift 版本編譯的應(yīng)用程序和庫(kù)之間實(shí)現(xiàn)兼容)的一個(gè)中間階段。ABI 的特性在集成進(jìn)最終的 ABI 之前會(huì)接收社區(qū)的大量反饋慨畸。

三莱坎、語(yǔ)言演進(jìn)

在這個(gè)版本中有很多新的語(yǔ)言特性。例如寸士,隨機(jī)數(shù)生成檐什,動(dòng)態(tài)成員查找等等

3.1 隨機(jī)數(shù)生成

3.1.1 隨機(jī)數(shù)生成

arc4random_uniform(_:) 返回一個(gè) 0 - 9 之間的隨機(jī)數(shù)字。這種實(shí)現(xiàn)方式有兩個(gè)問(wèn)題:

  • 需要引入 Foundation 框架弱卡,在 Linux 下無(wú)法工作乃正。
  • Linux 上的隨機(jī)數(shù)生成會(huì)產(chǎn)生模偏差(有取模的過(guò)程,更容易隨機(jī)到小的數(shù))婶博。
// Swift 4.1
let digit = Int(arc4random_uniform(10))

Swift 4.2 在標(biāo)準(zhǔn)庫(kù)中添加了隨機(jī)數(shù)的 API SE-0202

// Swift 4.2
/ 1  
let digit = Int.random(in: 0..<10)
?
// 2
if let anotherDigit = (0..<10).randomElement() {
 print(anotherDigit)
} else {
 print("Empty range.")
}
?
// 3
let double = Double.random(in: 0..<1)
let float = Float.random(in: 0..<1)
let cgFloat = CGFloat.random(in: 0..<1)
let bool = Bool.random()

注:randomElement() 如果 range 是空瓮具,返回 nil

3.1.2 數(shù)組隨機(jī)

Swift 4.1 數(shù)組隨機(jī)也是采用 C 函數(shù)的形式,這種方式會(huì)存在上面提到的問(wèn)題,而且會(huì)存在 Int 和 Int32 轉(zhuǎn)換的問(wèn)題名党。

let playlist = ["Nothing Else Matters", "Stairway to Heaven", "I Want to Break Free", "Yesterday"]
let index = Int(arc4random_uniform(UInt32(playlist.count)))
let song = playlist[index]

Swift 4.2 采用了更加簡(jiǎn)單直接的方式叹阔。

if let song = playlist.randomElement() {
 print(song)
} else {
 print("Empty playlist.")
}
3.1.3 洗牌算法

Swift 4.1 不包含任何集合的洗牌算法,所以要采用比較曲折的方式來(lái)實(shí)現(xiàn)传睹。

// 1
let shuffledPlaylist = playlist.sorted{ _, _ in arc4random_uniform(2) == 0 }
?
// 2
var names = ["Cosmin", "Oana", "Sclip", "Nori"]
names.sort { _, _ in arc4random_uniform(2) == 0 }

Swift 4.2 提供了更加高效更加優(yōu)雅的實(shí)現(xiàn) Shuffling Algorithms

let shuffledPlaylist = playlist.shuffled()
names.shuffle()

注:使用 shuffled() 來(lái)創(chuàng)建一個(gè)洗牌后的數(shù)組耳幢。使用 shuffle) 來(lái)將數(shù)組洗牌。

3.2 動(dòng)態(tài)成員查找

Swift 4.1 使用下面的方式實(shí)現(xiàn)自定義下標(biāo)操作蒋歌。

class Person {
  let name: String
  let age: Int
  private let details: [String: String]
  
  init(name: String, age: Int, details: [String: String]) {
    self.name = name
    self.age = age
    self.details = details
  }
  
  subscript(key: String) -> String {
    switch key {
      case "info":
        return "\(name) is \(age) years old."
      default:
        return details[key] ?? ""
    }
  }
}

let details = ["title": "Author", "instrument": "Guitar"]
let me = Person(name: "Cosmin", age: 32, details: details)
me["info"]   // "Cosmin is 32 years old."
me["title"]  // "Author"

Swift 4.2 使用動(dòng)態(tài)成員查找來(lái)提供點(diǎn)語(yǔ)法來(lái)實(shí)現(xiàn)下標(biāo)調(diào)用 Dynamic Member Lookup

// 1
@dynamicMemberLookup
class Person {
  let name: String
  let age: Int
  private let details: [String: String]
  
  init(name: String, age: Int, details: [String: String]) {
    self.name = name
    self.age = age
    self.details = details
  }
  
  // 2
  subscript(dynamicMember key: String) -> String {
    switch key {
      case "info":
        return "\(name) is \(age) years old."
      default:
        return details[key] ?? ""
    }
  }
}

// 3
me.info   // "Cosmin is 32 years old." 
me.title  // "Author"

使用步驟:

  • 標(biāo)記 Person 為 @dynamicMemberLookup 使下標(biāo)可以使用點(diǎn)語(yǔ)法
  • 遵守 @dynamicMemberLookup 實(shí)現(xiàn) subscript(dynamicMember:) 方法
  • 使用點(diǎn)語(yǔ)法調(diào)用之前定義的下標(biāo)

注:編譯器會(huì)在運(yùn)行時(shí)動(dòng)態(tài)評(píng)估下標(biāo)的調(diào)用帅掘,這樣就可以寫(xiě)出像 Python 或者 Ruby 等腳本語(yǔ)言一樣類(lèi)型安全的代碼。

動(dòng)態(tài)成員查找不會(huì)和類(lèi)的屬性混淆堂油。

me.name // "Cosmin"
me.age // 32

可以使用點(diǎn)語(yǔ)法而非下標(biāo)來(lái)調(diào)用 name 和 age修档。而且派生類(lèi)可以繼承基類(lèi)的動(dòng)態(tài)成員查找。

@dynamicMemberLookup
class Vehicle {
  let brand: String
  let year: Int
  
  init(brand: String, year: Int) {
    self.brand = brand
    self.year = year
  }
  
  subscript(dynamicMember key: String) -> String {
    return "\(brand) made in \(year)."
  }
}

class Car: Vehicle {}

let car = Car(brand: "BMW", year: 2018)
car.info  // "BMW made in 2018."

可以通過(guò)協(xié)議拓展給已有類(lèi)型添加動(dòng)態(tài)成員查找

// 1
@dynamicMemberLookup
protocol Random {}

// 2
extension Random {
  subscript(dynamicMember key: String) -> Int {
    return Int.random(in: 0..<10)
  }
}

// 3
extension Int: Random {}

// 4
let number = 10
let randomDigit = String(number.digit)
let noRandomDigit = String(number).filter { String($0) != randomDigit }

3.3 枚舉實(shí)例集合

Swift 4.1 默認(rèn)沒(méi)有提供訪問(wèn)枚舉實(shí)例集合的方式府框,所以實(shí)現(xiàn)方式不是很優(yōu)雅吱窝。

enum Seasons: String {
  case spring = "Spring", summer = "Summer", autumn = "Autumn", winter = "Winter"
}

enum SeasonType {
  case equinox
  case solstice
}

let seasons = [Seasons.spring, .summer, .autumn, .winter]
for (index, season) in seasons.enumerated() {
  let seasonType = index % 2 == 0 ? SeasonType.equinox : .solstice
  print("\(season.rawValue) \(seasonType).")
}

為了解決這個(gè)問(wèn)題,Swift 4.2 給枚舉類(lèi)型添加了實(shí)例數(shù)組迫靖。

// 1
enum Seasons: String, CaseIterable {
  case spring = "Spring", summer = "Summer", autumn = "Autumn", winter = "Winter"
}

enum SeasonType {
  case equinox
  case solstice
}

// 2
for (index, season) in Seasons.allCases.enumerated() {
  let seasonType = index % 2 == 0 ? SeasonType.equinox : .solstice
  print("\(season.rawValue) \(seasonType).")
}

如果枚舉中包含 unavailable院峡,需要將 availablecase 手動(dòng)維護(hù)協(xié)議中的 allCases

enum Days: CaseIterable {
  case monday, tuesday, wednesday, thursday, friday
  
  @available(*, unavailable)
  case saturday, sunday
  
  static var allCases: [Days] {
    return [.monday, .tuesday, .wednesday, .thursday, .friday]
  }
}

allCases 中只能添加 weekdays系宜,因?yàn)?saturdaysunday 被標(biāo)記為各個(gè)平臺(tái)不可用照激。
枚舉實(shí)例數(shù)組中也可以添加有關(guān)聯(lián)值的實(shí)例。

enum BlogPost: CaseIterable {
  case article
  case tutorial(updated: Bool)
  
  static var allCases: [BlogPost] {
    return [.article, .tutorial(updated: true), .tutorial(updated: false)]
  }
}

3.4 新的序列方法

Swift 4.1 中的 Sequence 定義了查找指定元素的第一個(gè)索引位置或者滿(mǎn)足指定條件的第一個(gè)元素的方法盹牧。

let ages = ["ten", "twelve", "thirteen", "nineteen", "eighteen", "seventeen", "fourteen",  "eighteen", "fifteen", "sixteen", "eleven"]

if let firstTeen = ages.first(where: { $0.hasSuffix("teen") }), 
   let firstIndex = ages.index(where: { $0.hasSuffix("teen") }), 
   let firstMajorIndex = ages.index(of: "eighteen") {
  print("Teenager number \(firstIndex + 1) is \(firstTeen) years old.")
  print("Teenager number \(firstMajorIndex + 1) isn't a minor anymore.")
} else {
  print("No teenagers around here.")
}

Swift 4.2 為了實(shí)現(xiàn)一致性重構(gòu)了方法名

if let firstTeen = ages.first(where: { $0.hasSuffix("teen") }), 
   let firstIndex = ages.firstIndex(where: { $0.hasSuffix("teen") }), 
   let firstMajorIndex = ages.firstIndex(of:  "eighteen") {
  print("Teenager number \(firstIndex + 1) is \(firstTeen) years old.")
  print("Teenager number \(firstMajorIndex + 1) isn't a minor anymore.")
} else {
  print("No teenagers around here.")
}

Swift 4.1 也沒(méi)有定義查找指定元素的最后一個(gè)索引的位置和滿(mǎn)足指定條件的的最后一個(gè)元素等方法俩垃。在 Swift 4.1 中我們可能采用下面的方法來(lái)處理。

// 1
let reversedAges = ages.reversed()

// 2
if let lastTeen = reversedAges.first(where: { $0.hasSuffix("teen") }), 
   let lastIndex = reversedAges.index(where: { $0.hasSuffix("teen") })?.base, 
   let lastMajorIndex = reversedAges.index(of: "eighteen")?.base {
  print("Teenager number \(lastIndex) is \(lastTeen) years old.")
  print("Teenager number \(lastMajorIndex) isn't a minor anymore.")
} else {
  print("No teenagers around here.")
}

Swift 4.2 添加了相應(yīng)的方法汰寓,使用方式如下

if let lastTeen = ages.last(where: { $0.hasSuffix("teen") }), 
   let lastIndex = ages.lastIndex(where: { $0.hasSuffix("teen") }), 
   let lastMajorIndex = ages.lastIndex(of: "eighteen") {
  print("Teenager number \(lastIndex + 1) is \(lastTeen) years old.")
  print("Teenager number \(lastMajorIndex + 1) isn't a minor anymore.")
} else {
  print("No teenagers around here.")
}

3.5 檢測(cè)序列元素

Swift 4.1 中沒(méi)有檢查序列中所有元素是否滿(mǎn)足某個(gè)指定條件的方法口柳。不過(guò)你可以實(shí)現(xiàn)你自己的方法,例如下面檢測(cè)集合中的元素是否都是偶數(shù)有滑。

let values = [10, 8, 12, 20]
let allEven = !values.contains { $0 % 2 == 1 }

Swift 4.2 添加了新的方法跃闹,很好的簡(jiǎn)化了代碼,提升了可讀性毛好。

let allEven = values.allSatisfy { $0 % 2 == 0 }

3.6 條件遵守更新

Swift 4.2 給拓展和標(biāo)準(zhǔn)庫(kù)中添加一些條件遵守方面的改進(jìn)望艺。

3.6.1 拓展中的條件遵守

Swift 4.1 不能在拓展中自動(dòng)合成 Equatable 的協(xié)議實(shí)現(xiàn)。例子如下:

// 1
struct Tutorial : Equatable {
  let title: String
  let author: String
}

// 2
struct Screencast<Tutorial> {
  let author: String
  let tutorial: Tutorial
}

// 3 
extension Screencast: Equatable where Tutorial: Equatable {
  // 必須自己實(shí)現(xiàn) == 方法肌访,Swift 4.1 不會(huì)自動(dòng)合成
  static func ==(lhs: Screencast, rhs: Screencast) -> Bool {
    return lhs.author == rhs.author && lhs.tutorial == rhs.tutorial
  }
}

// 4
let swift41Tutorial = Tutorial(title: "What's New in Swift 4.1?", author: "Cosmin Pup?z?")
let swift42Tutorial = Tutorial(title: "What's New In Swift 4.2?", author: "Cosmin Pup?z?")
let swift41Screencast = Screencast(author: "Jessy Catterwaul", tutorial: swift41Tutorial)
let swift42Screencast = Screencast(author: "Jessy Catterwaul", tutorial: swift42Tutorial)
let sameScreencast = swift41Screencast == swift42Screencast

Swift 4.2 只需要遵守協(xié)議荣茫,不需要實(shí)現(xiàn)。因?yàn)榫幾g器會(huì)添加一個(gè)默認(rèn)的Equatable協(xié)議實(shí)現(xiàn)场靴。

extension Screencast: Equatable where Tutorial: Equatable {}

這個(gè)特性也同樣支持 HashableCodable

// 1
struct Tutorial: Hashable, Codable {
  let title: String
  let author: String
}

struct Screencast<Tutorial> {
  let author: String
  let tutorial: Tutorial
}

// 2
extension Screencast: Hashable where Tutorial: Hashable {}
extension Screencast: Codable where Tutorial: Codable {}

// 3
let screencastsSet: Set = [swift41Screencast, swift42Screencast]
let screencastsDictionary = [swift41Screencast: "Swift 4.1", swift42Screencast: "Swift 4.2"]

let screencasts = [swift41Screencast, swift42Screencast]
let encoder = JSONEncoder()
do {
  try encoder.encode(screencasts)
} catch {
  print("\(error)")
}
3.6.2 條件遵守運(yùn)行時(shí)查詢(xún)

Swift 4.2 實(shí)現(xiàn)條件遵守的動(dòng)態(tài)查詢(xún)≈及可以從下面的例子看出咧欣。

// 1
class Instrument {
  let brand: String
  
  init(brand: String = "") {
    self.brand = brand
  }
}

// 2
protocol Tuneable {
  func tune()
}

// 3
class Keyboard: Instrument, Tuneable {
  func tune() {
    print("\(brand) keyboard tuning.")
  }
}

// 4
extension Array: Tuneable where Element: Tuneable {
  func tune() {
    forEach { $0.tune() }
  }
}

// 5
let instrument = Instrument()
let keyboard = Keyboard(brand: "Roland")
let instruments = [instrument, keyboard]

// 6
if let keyboards = instruments as? Tuneable {
  keyboards.tune()
} else {
  print("Can't tune instrument.")
}

注:上面在條件遵循的運(yùn)行時(shí)檢測(cè)中,會(huì)輸出 "Can't tune instrument."轨帜,因?yàn)?Instrument類(lèi)型不遵守 Tuneable 協(xié)議魄咕,如果是兩個(gè) Keyboard 類(lèi)型就可以。
更多關(guān)于 Conditional Conformance 的內(nèi)容蚌父,參考 Swift 4.1 更新指北(譯)

3.6.3 Hashable 在標(biāo)準(zhǔn)庫(kù)中條件遵守增強(qiáng)

在 Swift 4.2 中可選值哮兰、數(shù)組、字典和區(qū)間當(dāng)他們的元素是 Hashable 的話(huà)苟弛,他們也是 Hashable喝滞。

struct Chord: Hashable {
  let name: String
  let description: String?
  let notes: [String]
  let signature: [String: [String]?]
  let frequency: CountableClosedRange<Int>
}

let cMajor = Chord(name: "C", description: "C major", notes: ["C", "E",  "G"], 
                   signature: ["sharp": nil,  "flat": nil], frequency: 432...446)
let aMinor = Chord(name: "Am", description: "A minor", notes: ["A", "C", "E"], 
                   signature: ["sharp": nil, "flat": nil], frequency: 440...446)
let chords: Set = [cMajor, aMinor]
let versions = [cMajor: "major", aMinor: "minor"]

3.7 Hashable 增強(qiáng)

Swift 4.1 中一般會(huì)像下面這樣實(shí)現(xiàn)自定義哈希函數(shù):

class Country: Hashable {
  let name: String
  let capital: String
  
  init(name: String, capital: String) {
    self.name = name
    self.capital = capital
  }
  
  static func ==(lhs: Country, rhs: Country) -> Bool {
    return lhs.name == rhs.name && lhs.capital == rhs.capital
  }
  
  var hashValue: Int {
    return name.hashValue ^ capital.hashValue &* 16777619
  }
}

let france = Country(name: "France", capital: "Paris")
let germany = Country(name: "Germany", capital: "Berlin")
let countries: Set = [france, germany]
let countryGreetings = [france: "Bonjour", germany: "Guten Tag"]

因?yàn)?code>countries是 Hashable ,所以可以添加到集合或者字典中膏秫。但是 hashValue 的實(shí)現(xiàn)很難理解并且也不高效右遭。Swift 4.2 通過(guò)定義了一個(gè)通用的哈希函數(shù)來(lái)解決這個(gè)問(wèn)題。

class Country: Hashable {
  let name: String
  let capital: String
  
  init(name: String, capital: String) {
    self.name = name
    self.capital = capital
  }
  
  static func ==(lhs: Country, rhs: Country) -> Bool {
    return lhs.name == rhs.name && lhs.capital == rhs.capital
  }

  func hash(into hasher: inout Hasher) {
    hasher.combine(name)
    hasher.combine(capital)
  }
}

Country 中使用 hash(into:) 來(lái)替代 hashValue缤削。這個(gè)函數(shù)使用 combine() 將屬性注入到 hasher中窘哈。
注:現(xiàn)在實(shí)現(xiàn)上很容易,并且性能要比之前的版本高亭敢。

3.8 集合中移除元素

在 Swift 4.1 中滚婉,想要從集合中移除一個(gè)指定的元素,通常會(huì)使用 filter(_:) 的實(shí)現(xiàn)方式帅刀,在 Swift 4.2 添加了 removeAll(_:)让腹。

// Swift 4.1
var greetings = ["Hello", "Hi", "Goodbye", "Bye"]
greetings = greetings.filter { $0.count <= 3 }
// Swift 4.2
greetings.removeAll { $0.count > 3 }

3.9 更改布爾值

在 Swift 4.1 中,我們通常會(huì)這樣實(shí)現(xiàn)

extension Bool {
  mutating func toggle() {
    self = !self
  }
}

var isOn = true
isOn.toggle()

Swift 4.2 給 Bool 增加了 toggle()方法

3.10 新的編譯器指令

Swift 4.2 定義了表述代碼問(wèn)題的編譯器指令

// 1
#warning("There are shorter implementations out there.")

let numbers = [1, 2, 3, 4, 5]
var sum = 0
for number in numbers {
  sum += number
}
print(sum)

// 2
#error("Please fill in your credentials.")

let username = ""
let password = ""
switch (username.filter { $0 != " " }, password.filter { $0 != " " }) {
  case ("", ""):
    print("Invalid username and password.")
  case ("", _):
    print("Invalid username.")
  case (_, ""):
    print("Invalid password.")
  case (_, _):
    print("Logged in succesfully.")
}
  • #warning 用來(lái)輸出警告信息劝篷,表示實(shí)現(xiàn)未完全完成
  • #error 強(qiáng)制其他開(kāi)發(fā)者填入 usernamepassword

3.11 新的指針函數(shù)

withUnsafeBytes(of:_:)withUnsafePointer(to:_:) 在 Swift 4.1 中只能用于變量哨鸭,所以必須拷貝一份。Swift 4.2 中該函數(shù)支持常量娇妓,不需要再保存值像鸡。

// Swift 4.1
let value = 10
var copy = value
withUnsafeBytes(of: &copy) { pointer in print(pointer.count) }
withUnsafePointer(to: &copy) { pointer in print(pointer.hashValue) }
// Swift 4.2
withUnsafeBytes(of: value) { pointer in print(pointer.count) }
withUnsafePointer(to: value) { pointer in print(pointer.hashValue) }

3.12 Memory Layout 更新

Swift 4.2 使用 keypath 查找存儲(chǔ)屬性的內(nèi)存布局 [SE-0210],具體做法如下:

// 1
struct Point {
  var x, y: Double
}

// 2
struct Circle {
  var center: Point
  var radius: Double
  
  var circumference: Double {
    return 2 * .pi * radius
  }
  
  var area: Double {
    return .pi * radius * radius
  }
}

// 3
if let xOffset = MemoryLayout.offset(of: \Circle.center.x), 
   let yOffset = MemoryLayout.offset(of: \Circle.center.y), 
   let radiusOffset = MemoryLayout.offset(of: \Circle.radius) {
  print("\(xOffset) \(yOffset) \(radiusOffset)")
} else {
  print("Nil offset values.")
}

// 4
if let circumferenceOffset = MemoryLayout.offset(of: \Circle.circumference), 
   let areaOffset = MemoryLayout.offset(of: \Circle.area) {
  print("\(circumferenceOffset) \(areaOffset)")
} else {
  print("Nil offset values.")

注:可以通過(guò) keypath 返回存儲(chǔ)屬性的內(nèi)存偏移哈恰。計(jì)算屬性返回 nil只估,因?yàn)闆](méi)有存儲(chǔ)關(guān)聯(lián)。

3.13 模塊中的內(nèi)聯(lián)函數(shù)

在 Swift 4.1 中着绷,不允許在自己的模塊中定義內(nèi)聯(lián)函數(shù)蛔钙。依次選擇 View ? Navigators ? Show Project Navigator, 右鍵單擊 Sources and 選擇 New File。重命名文件為 FactorialKit.swift 并且替換為下面代碼塊中的代碼荠医。

public class CustomFactorial {
  private let customDecrement: Bool
  
  public init(_ customDecrement: Bool = false) {
    self.customDecrement = customDecrement
  }
  
  private var randomDecrement: Int {
    return arc4random_uniform(2) == 0 ? 2 : 3
  }
  
  public func factorial(_ n: Int) -> Int {
    guard n > 1 else {
      return 1
    }
    let decrement = customDecrement ? randomDecrement : 1
    return n * factorial(n - decrement)
  }
}

在 Swift 4.2 中定義為內(nèi)聯(lián)的函數(shù)會(huì)更加高效吁脱,所以將 FactorialKit.swift 的代碼替換如下桑涎。

public class CustomFactorial {
  @usableFromInline let customDecrement: Bool
  
  public init(_ customDecrement: Bool = false) {
    self.customDecrement = customDecrement
  }
  
  @usableFromInline var randomDecrement: Int {
    return Bool.random() ? 2 : 3
  }
  
  @inlinable public func factorial(_ n: Int) -> Int {
    guard n > 1 else {
      return 1
    }
    let decrement = customDecrement ? randomDecrement : 1
    return n * factorial(n - decrement)
  }
}

四、其他更新

下面是 Swift 4.2 中的一些其他改變

4.1 Swift Package Manager 更新
4.1.1 定義 Package 的 Swift 版本

Swift 4.1 在 Package.swift 中定義了swiftLanguageVersions兼贡,所以可以在 packages 中定義主版本攻冷。

let package = Package(name: "Package", swiftLanguageVersions: [4])

Swift 4.2 中也能通過(guò)SwiftVersion定義小版本 [SE-0209]

let package = Package(name: "Package", swiftLanguageVersions: [.v4_2])

能夠通過(guò).version(_:)定義之后的版本

let package = Package(name: "Package", swiftLanguageVersions: [.version("5")])
4.1.2 Packages 定義本地版本

在 Swift 4.1 中,可以使用倉(cāng)庫(kù)鏈接為 Package 定義依賴(lài)遍希。如果有相互關(guān)聯(lián)的 Package等曼,就會(huì)產(chǎn)生額外的問(wèn)題,所以 Swift 4.2 中引入了本地路徑而提案[SE-0201]凿蒜。

4.1.3 給 Package 添加系統(tǒng)庫(kù) Target
4.1.4 Swift 4.1 中系統(tǒng)模塊包需要分倉(cāng)庫(kù)禁谦,這樣包管理很難用 ,所以 Swift 4.2 使用系統(tǒng)庫(kù) Target 來(lái)實(shí)現(xiàn) [SE-0208]
4.2 移除隱式解包可選值

在 Swift 4.1 中废封,你可以在嵌套類(lèi)型中使用隱式解包可選值州泊。

let favoriteNumbers: [Int!] = [10, nil, 7, nil]
let favoriteSongs: [String: [String]!] = ["Cosmin": ["Nothing Else Matters", "Stairway to Heaven"], "Oana": nil] 
let credentials: (usermame: String!, password: String!) = ("Cosmin", nil)

Swift 4.2 從數(shù)組、字典和元祖中移除了隱式解包可選值 SE-0054

let favoriteNumbers: [Int?] = [10, nil, 7, nil]
let favoriteSongs: [String: [String]?] = ["Cosmin": ["Nothing Else Matters", "Stairway to Heaven"], "Oana": nil] 
let credentials: (usermame: String?, password: String?) = ("Cosmin", nil)

五虱饿、未來(lái)愿景

可以從這個(gè)教程中下載最終的 Playground拥诡。Swift 4.2 在 Swift 4.1 諸多特性的基礎(chǔ)上進(jìn)一步做了改進(jìn),而且為了2019 年初 Swift 5 的 ABI 穩(wěn)定做好準(zhǔn)備氮发】嗜猓可以從 Swift CHANGELOG 或者 Swift standard library diffs 中了解更多關(guān)于這個(gè)版本的改變。也可以從 Swift Evolution 中看出 Swift 5 的改變爽冕。你可以給正在審查的提案提交反饋或者自己提交一個(gè)提案仇祭。到目前為止對(duì) Swift 4.2 有什么喜歡或者不喜歡的地方【被可以在論壇中參與討論乌奇。

六、感謝原作者 Cosmin Pup?z?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末眯娱,一起剝皮案震驚了整個(gè)濱河市礁苗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌徙缴,老刑警劉巖试伙,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異于样,居然都是意外死亡疏叨,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人洼畅,你說(shuō)我怎么就攤上這事⌒阌郑” “怎么了单寂?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)涮坐。 經(jīng)常有香客問(wèn)我凄贩,道長(zhǎng),這世上最難降的妖魔是什么袱讹? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮昵时,結(jié)果婚禮上捷雕,老公的妹妹穿的比我還像新娘。我一直安慰自己壹甥,他們只是感情好救巷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著句柠,像睡著了一般浦译。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上溯职,一...
    開(kāi)封第一講書(shū)人閱讀 51,482評(píng)論 1 302
  • 那天精盅,我揣著相機(jī)與錄音,去河邊找鬼谜酒。 笑死叹俏,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的僻族。 我是一名探鬼主播粘驰,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼述么!你這毒婦竟也來(lái)了蝌数?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤度秘,失蹤者是張志新(化名)和其女友劉穎顶伞,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體敷钾,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡枝哄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了阻荒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挠锥。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖侨赡,靈堂內(nèi)的尸體忽然破棺而出蓖租,到底是詐尸還是另有隱情粱侣,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布蓖宦,位于F島的核電站齐婴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏稠茂。R本人自食惡果不足惜柠偶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望睬关。 院中可真熱鬧诱担,春花似錦、人聲如沸电爹。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)丐箩。三九已至摇邦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間屎勘,已是汗流浹背施籍。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留挑秉,地道東北人法梯。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像犀概,于是被迫代替她去往敵國(guó)和親立哑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354

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

  • 1姻灶、通過(guò)CocoaPods安裝項(xiàng)目名稱(chēng)項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請(qǐng)求組件 FMDB本地?cái)?shù)據(jù)庫(kù)組件 SD...
    陽(yáng)明先生_X自主閱讀 15,980評(píng)論 3 119
  • 這兩天的天氣真熱,熱的連飯都吃不下了曾沈。 喜歡在吃飯的時(shí)候和同事聊聊天这嚣,看到新聞?wù)f,在印度這么熱的天有1500人受不...
    安梓閱讀 262評(píng)論 0 1
  • 明明什么也沒(méi)有塞俱,可心里卻像那江南的煙雨一樣姐帚,濕漉漉的。
    阿荒姑娘閱讀 48評(píng)論 0 0
  • 雨夜漫漫障涯,無(wú)心睡眠罐旗。下雨的時(shí)候膳汪,總會(huì)有一種莫名哀怨的矯情情緒會(huì)從潛藏在身體的各個(gè)角落偷偷摸摸鉆出來(lái)慢慢占據(jù)...
    喵美男閱讀 242評(píng)論 0 0