Swift5.0新特性更新

Swift

  • 原文博客地址: Swift 5.0新特性更新
  • 期待已久的Swift 5.0終於來啦, Swift 5.0Swift中最備受關注的一個版本, 傳說中ABI穩定的版本
  • 隨着Xcode Bate 10.2的發佈, Swift 5.0也發佈了測試版, 相信也帶來了不少優化和改進
  • 下面運行環境都是在Xcode Bate 10.2環境中進行的

新特性

dynamicCallable

  • SE-0216
  • @dynamicCallableSwift添加了一個新屬性, 容許使用一個簡單的語法糖調用函數同樣調用命名類型
  • 若是須要添加@dynamicCallable屬性, 就必需要實現如下方法中的一個或者兩個
func dynamicallyCall(withArguments args: [Int]) -> Double

func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, Int>) -> Double
複製代碼
  • 注意點:
  • 除了接受各類輸入外,您還能夠爲各類輸出提供多個重載, 自定義返回值, 能夠是String, Int等等......
  • KeyValuePairs的使用和介紹, 沒有使用過的可參考

下面看一個例子, RandomNumberGenerator生成一個隨機數git

Swift 5.0以前的定義和調用方式github

// 定義方式
struct RandomNumberGenerator {
    func generate(numberOfZeroes: Int) -> Double {
        let maximum = pow(10, Double(numberOfZeroes))
        return Double.random(in: 0...maximum)
    }
}


// 調用方式
let random = RandomNumberGenerator()
let num = random.generate(numberOfZeroes: 2)
print(num)
複製代碼

Swift 5.0使用@dynamicCallable屬性編程

// 定義方式
@dynamicCallable
struct RandomNumberGenerator {
    func dynamicallyCall(withArguments args: [Int]) -> Double {
        let numberOfZeroes = Double(args.first ?? 0)
        let maximum = pow(10, numberOfZeroes)
        return Double.random(in: 0...maximum)
    }
}


// 調用方式
let random = RandomNumberGenerator()
let num = random(2)
// random(2)等同於random.dynamicallyCall(withArguments: [2])
print(num)
複製代碼
  • @dynamicCallable使用注意事項
  • 能夠將它應用於結構,枚舉,類和協議。
  • 若是你實現withKeywordArguments:而且沒有實現withArguments:,你仍然能夠在沒有參數標籤的狀況下調用
  • 若是你的實現withKeywordArguments:withArguments:時標記爲throw,則調用該類型也將被拋出throw
  • 擴展名沒法添加@dynamicCallable,只能添加到主要類型上
  • 仍然能夠爲你定義的類型添加其餘方法和屬性,而且可以正常使用

WritableKeyPath

  • SE-0227
  • 添加引用標識鍵路徑的功能,該路徑指的是應用它的整個輸入值
  • Swift中的每一個值都有一個特殊的僞屬性.self,它指的是整個值
let id = \Int.self  
var x = 1
print(id)   ////Swift.WritableKeyPath<Swift.Int, Swift.Int>
x.self = 2
print(x)   //2
print(x.self)  //2

print(x[keyPath: id])  //2
x[keyPath: id] = 3
print(x[keyPath: id])  //3
複製代碼

可選參數

Swift 5以前,能夠編寫一個帶有可變參數的枚舉, 可是在Swift 5開始, 調用時會報錯, 以下swift

enum X {
    // 此處定義切沒有調用時不會報錯
    case foo(bar: Int...) 
}

func baz() -> X {
    // 此處調用時會報錯
    return .foo(bar: 0, 1, 2, 3) 
} 
複製代碼

Swift 5以後, 上述定義改爲數組參數, 而不是可變參數, 以下api

enum X {
    case foo(bar: [Int]) 
} 

func baz() -> X {
    return .foo(bar: [0, 1, 2, 3]) 
} 
複製代碼

Raw Strings

\處理

  • SE-0200增長了建立原始字符串的功能,其中反斜槓和引號被解釋爲文字符號,而不是轉義字符或字符串終止符
  • 單行字符串文字能夠用反斜槓填充, 以保證原字符串, 不然會報錯
// 文字引用類型
// 錯誤寫法
let quote = "Alice: "How long is forever?" White Rabbit: "Sometimes, just one second.""
// 正確寫法
let quote1 = "Alice: \"How long is forever?\" White Rabbit: \"Sometimes, just one second.\""


// 正則表法式類型
// 錯誤寫法
let ucCaseCheck = "enum\s+.+\{.*case\s+[:upper:]"
// 正確寫法
let ucCaseCheck = "enum\\s+.+\\{.*case\\s+[:upper:]"
複製代碼

#處理

  • 要使用原始字符串, 可以使用#將字符串包裹起來
  • #字符串開頭和結尾的符號成爲字符串分隔符的一部分,所以以下Swift理解「rain」「Spain」周圍的獨立引號應該被視爲文字引號而不是結束字符串
  • 原始字符串也容許使用反斜槓, 可是將反斜槓視爲字符串中的文字字符,而不是轉義字符
let rain = #"The "rain" in "Spain" falls mainly on the Spaniards."#

let keypaths = #"Swift keypaths such as \Person.name hold uninvoked references to properties."#

let answer = 42
let dontpanic = #"The answer to life, the universe, and everything is \#(answer)."#
複製代碼

注意: 上面使用\#(answer)引用變量而不是\(answer), 由於在用#包裹的字符串中反斜槓將會被失敗別爲文字字符而不是轉義字符, 因此必須額外的添加#數組

##處理

  • 在字符串的開頭和結尾使用#處理, 在字符串中可使用反斜槓等特殊字符, 那若是字符串中須要使用#, 又該如何處理??
  • 使用#包裹字符串, 默認會以#爲字符串的結束符號, #後面的文字將再也不處理, 在這種狀況下, 咱們會使用##處理
  • 注意: 字符串的開頭和結尾的標識必須同樣
let str = ##"My dog said "woof"#gooddog"##

複製代碼

多行字符串

原始字符串與Swift的多行字符串系統徹底兼容 - 只需用於#"""啓動,而後"""#結束xcode

let multiline = #"""
    The answer to life,
    the universe,
    and everything is \#(answer).
    """#
複製代碼

try?嵌套

先看下面代碼bash

struct User {
    var id: Int

    init?(id: Int) {
        if id < 1 {
            return nil
        }

        self.id = id
    }

    func getMessages() throws -> String {
        // complicated code here
        return "No messages"
    }
}
複製代碼

Swift4.2及其以前的版本中微信

let user = User(id: 1)
// 這裏獲得的message的類型是: let messages: String??
let messages = try? user?.getMessages()

// 若是咱們想獲得非可選值就須要
print((messages ?? "") ?? "")
// 或者屢次強解, 固然不建議強解寫法
print(messages!!)
複製代碼
  • Swift4.2及其以前的版本中, 上面返回的是一個2層嵌套的可選值, 若是有多層嵌套處理起來也是至關更麻煩的
  • Swift 5中就完美的解決了這個問題, 若是當前值是可選的, 那麼try?將不會將值包裝在可選值中, 所以最終結果只是一個String?
  • 所以在Swift 5中不管有多少可嵌套的可選最後, 返回值永遠只是一個可選值
  • 一樣,若是你使用了可選的連接as?,你仍然只有一個級別的可選性
let user = User(id: 1)
// 類型: let messages: String?
let messages = try? user?.getMessages()

print(messages ?? "")
複製代碼

isMultiple

  • SE-0225爲整數類型添加了一個方法isMultiple
  • 該方法能夠檢查一個整數是否爲另外一個整數的倍數
let rowNumber = 4

if rowNumber.isMultiple(of: 2) {
    print("Even")
} else {
    print("Odd")
}

// 該方法等效於
if rowNumber % 2 == 0 {}
複製代碼

count

  • SE-0220
  • Swift以前的版本中, 有一個函數filter能夠過濾出數組中符合條件的的元素, 組成一個新的數組, 詳細使用可參考Swift函數式編程之高級用法
  • Swift 5中新增了一個函數count(where:), 能夠獲取數組中符合條件的元素的個數
let arr = [1, 2, 34, 5, 6, 7, 8, 12, 45, 6, 9]

let filter = arr.filter({ $0 > 10 })
print(filter)  //[34, 12, 45]

let count = arr.count(where: { $0 > 10 })
print(count)  // 3
複製代碼

compactMapValues

  • Swift4.x的版本有兩個函數compactMapmapValues
  • compactMap: 返回一個操做後獲得的新的數組, 相似flatMap
  • mapValues: 字典中的函數, 對字典的value值執行操做, 返回改變value後的新的字典
let times = [
    "first": 2,
    "second": 43,
    "three": 12,
    "four": 3
]

let compact = times.compactMap({ $0.value > 10 })
print(compact)
// [true, false, true, false]

let mapValues = times.mapValues({ $0 + 2 })
print(mapValues)
// ["second": 45, "first": 4, "three": 14, "four": 5]
複製代碼
  • SE-0218Swift 5中新增了一個函數compactMapValues
  • compactMapValues是將上述兩個方法的功能合併在一塊兒, 返回一個對value操做後的新字典, 而且自動過濾不符合條件的鍵值對
let times1 = [
    "Hudson": "38",
    "Clarke": "42",
    "Robinson": "35",
    "Hartis": "DNF"
]

let comMap2 = times1.compactMapValues({ Int($0) })
print(comMap2)
// ["Clarke": 42, "Robinson": 35, "Hudson": 38]
複製代碼

SubSequence

  • Sequence協議再也不具備SubSequence關聯類型。先前返回SubSequenceSequence方法如今會返回具體類型
  • 使用SubSequenceSequence擴展應該修改成相似地使用具體類型,或者修改成Collection的擴展,在CollectionSubSequence仍然可用
// swift 5不在支持
extension Sequence {
    func dropTwo() -> SubSequence {
        return self.dropFirst(2)
    }
}


// 建議改成
extension Sequence {
    func dropTwo() -> DropFirstSequence<Self> { 
        return self.dropFirst(2)
    }
}

// 或者
extension Collection {
    func dropTwo() -> SubSequence {
        return self.dropFirst(2)
    }
}
複製代碼

其餘相關更新

SE-0214

DictionaryLiteral類型重命名爲KeyValuePairsapp

SE-0238

  • 在使用Swift 5軟件包管理器時,Targets能夠聲明一些經常使用的針對特定目標的build settings設置
  • 新設置也能夠基於平臺和構建配置進行條件化處理。包含的構建設置支持SwiftC語言定義,C語言頭文件搜索路徑,連接庫和連接框架

SE- 0236

  • 在使用Swift 5時, 能夠自定義所支持的最低版本號, 若是該項目的依賴包所支持的最低版本大於項目的最低版本號, 則項目會報錯

SR-695

Swift 5中再也不支持返回Self的類方法

// 不在支持
class Base { 
    class func factory() -> Self { /*...*/ }
} 
複製代碼

SR-631

不一樣文件中的擴展名沒法相互識別

class FirstClass { }

extension FirstClass {
    class SecondClass { }
}

// 這裏將會報錯: "SecondClass is not a member type of FirstClass"
extension FirstClass.SecondClass { 
    class ThirdClass { }
}
複製代碼

SR-7251

Swift 5中, 在所聲明的類裏面, 所聲明的變量名不能和類名同樣

struct S {}
extension S {
  static var i: Int { return 0 }
  struct i {} // error: 「i」的聲明無效
}

// 下面的方式是沒有問題的
struct S1<T> {}
extension S1 {
  static var i: Int { return 0 }
  struct i {} // This is fine!
}
複製代碼

參考文獻


歡迎您掃一掃下面的微信公衆號,訂閱個人博客!

微信公衆號
相關文章
相關標籤/搜索