- 原文博客地址Swift 4.2 新特性更新
-
Github
地址--Swift 4.2代碼地址 -
Swift 4.2
是Swift 4.0
發(fā)布以來的第二次小更新, 繼上次Xcode 9.3
和Swift 4.1
發(fā)布以來也有倆月有余 - 上個(gè)版本Swift 4.1 的新特性中介紹了條件一致性和哈希索引等相關(guān)更新
- 隨著
Xcode Bate 10
的發(fā)布,Swift 4.2
也發(fā)布了測試版, 相信也帶來了很多優(yōu)化和改進(jìn) - 下面運(yùn)行環(huán)境都是在
Xcode Bate 10
環(huán)境中進(jìn)行的
CaseInterable
協(xié)議
-
SE-0194介紹了在
Swift 4.2
中新增的一個(gè)新的CaseIterable
協(xié)議 - 定義的枚舉遵循
CaseIterable
協(xié)議后, 編譯時(shí)Swift
會(huì)自動(dòng)合成一個(gè)allCases
屬性洗出,是包含枚舉的所有case
項(xiàng)的數(shù)組
enum NetState: CaseIterable {
case wifi
case hotWifi
case mobile
case none
}
之后我們?cè)谄渌胤秸{(diào)用改枚舉時(shí)就可以獲取到
allCase
屬性, 如下
print(NetState.allCases)
print("case個(gè)數(shù): " + "\(NetState.allCases.count)")
for item in NetState.allCases {
print(item)
}
// 輸出結(jié)果:
[__lldb_expr_9.NetState.wifi, __lldb_expr_9.NetState.hotWifi, __lldb_expr_9.NetState.mobile, __lldb_expr_9.NetState.none]
case個(gè)數(shù): 4
wifi
hotWifi
mobile
none
這個(gè)
allCases
的自動(dòng)合成僅替換沒有參數(shù)的case
值, 但是如果需要你需要所有case
值, 可以重寫allCases
屬性自己添加
enum FoodKind: CaseIterable {
//此處, 必須重寫allCases屬性, 否則報(bào)錯(cuò)
static var allCases: [FoodKind] {
return [.apple, .pear, .orange(look: false)]
}
case apple
case pear
case orange(look: Bool)
}
for item in FoodKind.allCases {
print(item)
}
/*
* 輸出結(jié)果:
apple
pear
orange(look: false)
*/
如果有枚舉項(xiàng)標(biāo)記為
unavailable
产雹,則默認(rèn)無法合成allCases
,只能依靠自己來手動(dòng)合成
enum CarKind: CaseIterable {
//當(dāng)有unavailable修飾的case值, 也必須重寫allCase屬性
static var allCases: [CarKind] {
return [.bwm, .ford]
}
case bwm
case ford
@available(*, unavailable)
case toyota
}
for item in CarKind.allCases {
print(item)
}
/*
輸出結(jié)果:
bwm
ford
*/
#warning
和#error
編譯指令
-
SE-0196介紹新的編譯指令來強(qiáng)制
Xcode
在build
時(shí)生成警告或錯(cuò)誤信息 - 這兩個(gè)指令是
#warning
和#error
胸完,前者會(huì)強(qiáng)制Xcode
在生成你的代碼時(shí)發(fā)出一個(gè)警告务冕,后者會(huì)發(fā)出一個(gè)編譯錯(cuò)誤這樣你的代碼就完全不能編譯 -
#warning
主要用于提醒你或者別人一些工作還沒有完成血当,Xcode
模板常使用#warning
標(biāo)記一些你需要替換成自己代碼的方法存根(method stubs
)幻赚。 -
#error
主要用于如果你發(fā)送一個(gè)庫禀忆,需要其他開發(fā)者提供一些數(shù)據(jù)臊旭。比如,一個(gè)網(wǎng)絡(luò)API
的認(rèn)證密碼箩退,你需要用戶輸入它們自己的密碼离熏,就使用#error
在繼續(xù)之前強(qiáng)制他們更改這行代碼
#warning
和#error
可以和已存的#if
編譯指令共同使用,并且只有在條件為true
時(shí)才會(huì)激活戴涝。例如:
#if os(macOS)
#error("MyLibrary is not supported on macOS.")
#endif
動(dòng)態(tài)成員查找
-
SE-0195介紹了一個(gè)方法滋戳,讓
Swift
更接近類似Python
的腳本語言, 讓Swift
可以以屬性訪問的方式調(diào)用下標(biāo)操作 - 這讓我們可以像
Python
一樣來訪問字典值,不過是以類型安全的方式, 其核心在于:-
@dynamicMemberLookup
: 可以讓Swift
以一種下標(biāo)方法去進(jìn)行屬性訪問 -
subscript(dynamicMember:)
:可以通過所請(qǐng)求屬性的字符串名得到啥刻,并且可以返回你想要的任何值
-
- 我們可以創(chuàng)建一個(gè)
Titan
結(jié)構(gòu)奸鸯,并且從一個(gè)字典讀取它的值
struct Titan {
subscript(dynamicMember member: String) -> String {
let properties = ["name": "Titanjun", "city": "Hang"]
return properties[member, default: "0"] //默認(rèn)值
}
}
- 可以看到上述代碼按字符串接收成員名字,并返回一個(gè)字符串可帽。
- 從內(nèi)部看它只是在一個(gè)字典中查找這個(gè)成員名字并返回它的值
- 即使取不到對(duì)應(yīng)的值, 也會(huì)以默認(rèn)值的形式返回, 上述結(jié)構(gòu)的代碼可以這么寫
let titan = Titan()
print(titan.name)
print(titan.city)
print(titan.age)
// 輸出:
Titanjun
Hang
0
處理多種不同的類型
- 上述
subscript(dynamicMember:)
方法必須返回一串字符娄涩,這體現(xiàn)了Swift
的類型安全性 - 如果你想要多種不同的類型, 就執(zhí)行不同的
subscript(dynamicMember:)
方法
@dynamicMemberLookup
struct Titan {
subscript(dynamicMember member: String) -> String {
let properties = ["name": "Titanjun", "city": "Hang"]
return properties[member, default: "0"] //默認(rèn)值
}
subscript(dynamicMember member: String) -> Int {
let properties = ["age": 20, "source": 99]
return properties[member, default: 0] //默認(rèn)值
}
}
需要注意的是: 這里取值的時(shí)候, 必須注明所取得值的類型
let titan = Titan()
let name: String = titan.name
let city: String = titan.city
let age: Int = titan.age
let jun: String = titan.jun
print(jun)
print("name = \(name), city = \(city), age = \(age)")
//輸出:
0
name = Titanjuun, city = Hang, age = 20
增強(qiáng)的條件一致性
條件一致性在Swift 4.1中引入,一個(gè)類型的所有元素如果符合Hashable
協(xié)議映跟,則類型自動(dòng)符合Hashable
協(xié)議
//定義Purchaseable協(xié)議
protocol Purchaseable {
func buy()
}
//定義一個(gè)符合該協(xié)議的結(jié)構(gòu)體
struct Book: Purchaseable {
func buy() {
print("You bought a book")
}
}
//數(shù)組遵循該協(xié)議, 并且每一個(gè)元素也遵循該協(xié)議
extension Array: Purchaseable where Element: Purchaseable {
func buy() {
for item in self {
item.buy()
}
}
}
下面我們?cè)赟wift 4.1中運(yùn)行如下代碼, 會(huì)發(fā)現(xiàn)崩潰
let items: Any = [Book(), Book(), Book()]
if let books = items as? Purchaseable {
books.buy()
}
- 如果你收到一種類型的數(shù)據(jù)蓄拣,想要檢查它是否可以被轉(zhuǎn)化為一個(gè)條件一致性協(xié)議, 這種在
Swift 4.1
中是不支持的, 但是在Swift 4.2
中卻可以很好的解決 - 另外, 對(duì)
Hashable
一致性自動(dòng)合并的支持在Swift 4.2
被大幅提高,來自Swift
標(biāo)準(zhǔn)庫的幾個(gè)內(nèi)置類型努隙,包括optionals
,arrays
,dictionaries
和ranges
, 現(xiàn)在當(dāng)他們的元素符合Hashable
時(shí)會(huì)自動(dòng)符合Hashable
協(xié)議
本地集合元素移除
SE-0197介紹一個(gè)新的removeAll(where:)
方法, 高效地執(zhí)行根據(jù)條件刪除操作
var pythons = ["John", "Michael", "Graham", "Terry", "Eric", "Terry"]
pythons.removeAll { $0.hasPrefix("Terry") }
print(pythons)
//輸出: ["John", "Michael", "Graham", "Eric"]
對(duì)比
filter
過濾方法
var python2 = ["John", "Michael", "Graham", "Terry", "Eric", "Terry"]
python2 = python2.filter { !$0.hasPrefix("Terry") }
print(python2)
這并不是非常有效地使用內(nèi)存球恤,它指定了你不想要的東西,而不是你想要的東西
隨機(jī)數(shù)字的生成和洗牌
-
SE-0202中
Swift
引入了新的隨機(jī)數(shù)API
- 可以通過調(diào)用
random()
隨機(jī)數(shù)方法來生成一個(gè)隨機(jī)數(shù), 只需提供一個(gè)隨機(jī)數(shù)范圍即可
let ranInt = Int.random(in: 0..<5)
let ranFloat = Float.random(in: 0..<5)
let ranDouble = Double.random(in: 0..<5)
let ranCGFloat = CGFloat.random(in: 0..<5)
let ranBOOL = Bool.random()
對(duì)數(shù)組進(jìn)行重新洗牌
SE-0202
還支持使用新方法shuffle()
和shuffled()
方法對(duì)數(shù)組元素進(jìn)行重新隨機(jī)排序
var albums = ["Red", "1989", "Reputation"]
// 沒有返回值
albums.shuffle()
// 有返回值, 重新返回一個(gè)數(shù)組
let shuffled = albums.shuffled()
獲取數(shù)組中的一個(gè)隨機(jī)元素
randomElement()
: 數(shù)組的一個(gè)新方法, 如果數(shù)組部位空, 則返回?cái)?shù)組中的一個(gè)隨機(jī)元素, 否則返回nil
if let random = albums.randomElement() {
print("The random album is \(random).")
}
更簡單荸镊,更安全的哈希
-
SE-0206介紹了在
Swift 4.1
中簡化了我們使自定義類型符合Hashable
協(xié)議的方式 -
Swift 4.2
引入了一個(gè)新的Hasher
結(jié)構(gòu)咽斧,它提供了一個(gè)隨機(jī)播種的通用散列函數(shù)
struct iPad: Hashable {
var serialNumber: String
var capacity: Int
func hash(into hasher: inout Hasher) {
hasher.combine(serialNumber)
}
}
- 可以通過
combine()
重復(fù)調(diào)用將更多屬性添加到散列,并且添加屬性的順序會(huì)影響完成的散列值贷洲。 - 還可以將其
Hasher
用作獨(dú)立散列發(fā)生器:只要提供您想散列的任何值收厨,然后調(diào)用finalize()
以生成最終值
let first = iPad(serialNumber: "12345", capacity: 256)
let second = iPad(serialNumber: "54321", capacity: 512)
var hasher = Hasher()
hasher.combine(first)
hasher.combine(second)
let hash = hasher.finalize()
-
Hasher
每次散列對(duì)象時(shí)都會(huì)使用隨機(jī)種子,這意味著任何對(duì)象的散列值在您的應(yīng)用運(yùn)行之間有效地保證是不同的 - 這又意味著您添加到集合或字典中的元素很可能在您每次運(yùn)行應(yīng)用程序時(shí)都有不同的順序
檢查序列元素是否符合條件
SE-0207提供了allSatisfy()
一種檢查序列中的所有元素是否滿足條件的新方法
//判斷數(shù)組的所有元素是否全部大于85
let scores = [86, 88, 95, 92]
//返回一個(gè)BOOL
let passed = scores.allSatisfy({ $0 > 85 })
print(passed)
//輸出: true
布爾切換
SE-0199引入了一種新的toggle()
方法, 可以將布爾值取反, 實(shí)現(xiàn)代碼如下:
extension Bool {
mutating func toggle() {
self = !self
}
}
測試代碼
var isSwift = true
//toggle函數(shù)沒有返回值
isSwift.toggle()
print(isSwift)
- SE-0204介紹了數(shù)組中的獲取滿足條件的數(shù)組中的最后一個(gè)元素或者索引值
- 在
Swift 4.1
中我們只能取得first
值, 卻無法獲取數(shù)組中的最后一個(gè)值(或者要用大量代碼實(shí)現(xiàn)) - 在
Swift 4.2
中提供了last(where:)
和lastIndex(where:)
方法來獲取數(shù)組中滿足條件的最后的元素和索引值
//獲取滿足條件的數(shù)組中的第一個(gè)值
let a = [20, 30, 10, 40, 20, 30, 10, 40, 20]
print(a.first(where: { $0 > 25 }))
print(a.index(where: { $0 > 25 }))
print(a.index(of: 10))
//輸出:
30
1
2
Swift 4.2
中新增的last
函數(shù)
//在Swift4.1中
print((a.reversed().index(where: { $0 > 25 })?.base).map({ a.index(before: $0) }))
//輸出: 7
//Swift 4.2
//獲取滿足條件的元素
print(a.last(where: { $0 > 25 })) //40
//獲取滿足條件的元素的索引
print(a.lastIndex(where: { $0 > 25 })) //7
展望Swift 5.0
- 蘋果形容
Swift 4.2
為”為了實(shí)現(xiàn)Swift 5中ABI穩(wěn)定性的中繼點(diǎn)”, 想必Swift 4.1
和Swift 4.2
的發(fā)布也是為了Swift 5.0
做一個(gè)鋪墊 - 最后還是期待
Swift 5.0
能夠帶來更加穩(wěn)定的API