- 原文部落格地址: Swift 5.0新特性更新
- 期待已久的
Swift 5.0
終於來啦,Swift 5.0
是Swift
中最備受關注的一個版本, 傳說中ABI
穩定的版本 - 隨著
Xcode Bate 10.2
的釋出,Swift 5.0
也釋出了測試版, 相信也帶來了很多優化和改進 - 下面執行環境都是在
Xcode Bate 10.2
環境中進行的
新特性
dynamicCallable
- SE-0216
@dynamicCallable
為Swift
新增了一個新屬性, 允許使用一個簡單的語法糖呼叫函式一樣呼叫命名型別- 如果需要新增
@dynamicCallable
屬性, 就必須要實現以下方法中的一個或者兩個
func dynamicallyCall(withArguments args: [Int]) -> Double
func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, Int>) -> Double
複製程式碼
- 注意點:
- 除了接受各種輸入外,您還可以為各種輸出提供多個過載, 自定義返回值, 可以是
String
,Int
等等...... KeyValuePairs
的使用和介紹, 沒有使用過的可參考
下面看一個例子, RandomNumberGenerator
生成一個隨機數
Swift 5.0
之前的定義和呼叫方式
// 定義方式
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
開始, 呼叫時會報錯, 如下
enum X {
// 此處定義切沒有呼叫時不會報錯
case foo(bar: Int...)
}
func baz() -> X {
// 此處呼叫時會報錯
return .foo(bar: 0, 1, 2, 3)
}
複製程式碼
在Swift 5
之後, 上述定義改成陣列引數, 而不是可變引數, 如下
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
的多行字串系統完全相容 - 只需用於#"""
啟動,然後"""#
結束
let multiline = #"""
The answer to life,
the universe,
and everything is \#(answer).
"""#
複製程式碼
try?巢狀
先看下面程式碼
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
的版本有兩個函式compactMap
和mapValues
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-0218在
Swift 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
關聯型別。先前返回SubSequence
的Sequence
方法現在會返回具體型別- 使用
SubSequence
的Sequence
擴充套件應該修改為類似地使用具體型別,或者修改為Collection
的擴充套件,在Collection
中SubSequence
仍然可用
// 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
型別重新命名為KeyValuePairs
SE-0238
- 在使用
Swift 5
軟體包管理器時,Targets
可以宣告一些常用的針對特定目標的build settings
設定 - 新設定也可以基於平臺和構建配置進行條件化處理。包含的構建設定支援
Swift
和C
語言定義,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!
}
複製程式碼