Swift 4最全的新特性詳細(xì)解析(推薦)
引言
Swift,蘋(píng)果于2014年WWDC(蘋(píng)果開(kāi)發(fā)者大會(huì))發(fā)布的新開(kāi)發(fā)語(yǔ)言,可與Objective-C共同運(yùn)行于Mac OS和iOS平臺(tái),用于搭建基于蘋(píng)果平臺(tái)的應(yīng)用程序。Swift吸收了眾多現(xiàn)代編程語(yǔ)言的優(yōu)點(diǎn),盡力的提供簡(jiǎn)潔的編程語(yǔ)言和強(qiáng)大的功能。
WWDC 2017 給大家?guī)?lái)了很多驚喜。Swift 4 也伴隨著 Xcode 9 測(cè)試版來(lái)到了我們的面前,很多強(qiáng)大的新特性非常值得我們期待在正式項(xiàng)目中去使用它。因?yàn)?Swift 4 是開(kāi)源的,如果你關(guān)注 swift-evolution 這個(gè)項(xiàng)目的話,就應(yīng)該已經(jīng)提前了解到它的新特性了。本文參考了 WWDC 2017 以及各種資料,,從語(yǔ)法、字符串、標(biāo)準(zhǔn)庫(kù)、構(gòu)建過(guò)程等方面,把 Swift 4 的這些新特性一一列舉出來(lái)做介紹和分析,讓他們毫無(wú)保留地展現(xiàn)在你眼前,下面話不多說(shuō)了,來(lái)隨著小編一起看看詳細(xì)的介紹吧。
一、語(yǔ)法改進(jìn)
extension 中可以訪問(wèn) private 的屬性
考慮以下代碼:
struct Date: Equatable, Comparable {
private let secondsSinceReferenceDate: Double
static func ==(lhs: Date, rhs: Date) -> Bool {
return lhs.secondsSinceReferenceDate == rhs.secondsSinceReferenceDate
}
static func <(lhs: Date, rhs: Date) -> Bool {
return lhs.secondsSinceReferenceDate < rhs.secondsSinceReferenceDate
}
}
上面代碼定義了一個(gè) Date 結(jié)構(gòu)體,并實(shí)現(xiàn) Equatable 和 Comparable 協(xié)議。為了讓代碼更清晰,可讀性更好,一般會(huì)把對(duì)協(xié)議的實(shí)現(xiàn)放在單獨(dú)的 extension 中,這也是一種非常符合 Swift 風(fēng)格的寫(xiě)法,如下:
struct Date {
private let secondsSinceReferenceDate: Double
}
extension Date: Equatable {
static func ==(lhs: Date, rhs: Date) -> Bool {
return lhs.secondsSinceReferenceDate == rhs.secondsSinceReferenceDate
}
}
extension Date: Comparable {
static func <(lhs: Date, rhs: Date) -> Bool {
return lhs.secondsSinceReferenceDate < rhs.secondsSinceReferenceDate
}
}
但是在 Swift 3 中,編譯就報(bào)錯(cuò)了,因?yàn)?extension 中無(wú)法獲取到 secondsSinceReferenceDate 屬性,因?yàn)樗?private 的。于是在 Swift 3 中,必須把 private 改為 fileprivate。
struct Date {
fileprivate let secondsSinceReferenceDate: Double
}
...
但是如果用 fileprivate,屬性的作用域就會(huì)比我們需要的更大,可能會(huì)不小心造成屬性的濫用。
在 Swift 4 中,private 的屬性的作用域擴(kuò)大到了 extension 中,并且被限定在了 struct 和 extension 內(nèi)部,這樣就不需要再改成 fileprivate 了,這是最好的結(jié)果。
類型和協(xié)議的組合類型
考慮以下代碼:
protocol Shakeable {
func shake()
}
extension UIButton: Shakeable { /* ... */ }
extension UISlider: Shakeable { /* ... */ }
func shakeEm(controls: [???]) {
for control in controls where control.state.isEnabled {
}
control.shake()
}
在 Swift 3 中,這里的 ??? 應(yīng)該寫(xiě)什么呢?如果寫(xiě) UIControl,那么 control.shake() 就會(huì)報(bào)錯(cuò);如果寫(xiě) Shakeable,那么 control.state.isEnabled 就會(huì)報(bào)錯(cuò)。其實(shí)我們也可以這樣寫(xiě):
func shakeEm(controls: [UIControl]) {
for control in controls where control.isEnabled {
if control is Shakeable {
(control as! Shakeable).shake()
}
}
}
這樣寫(xiě)雖然可以跑通了,但是很丑陋。
在 Swift 4 中,可以把類型和協(xié)議用 & 組合在一起作為一個(gè)類型使用,就可以像下面這樣寫(xiě)了:
protocol Shakeable {
func shake()
}
extension UIButton: Shakeable { /* ... */ }
extension UISlider: Shakeable { /* ... */ }
func shakeEm(controls: [UIControl & Shakeable]) {
for control in controls where control.state.isEnabled {
control.shake()
}// Objective-C API
@interface NSCandidateListTouchBarItem<CandidateType> : NSTouchBarItem
@property (nullable, weak) NSView <NSTextInputClient> *client;
@end
}
把它聲明為了 UIControl & Shakeable 類型。OK,圓滿解決。
PS:
這個(gè)代碼例子是 WWDC 2017 的 PPT 中的,上面的代碼有點(diǎn)問(wèn)題,control.state.isEnabled 這句代碼中,state 是沒(méi)有 isEnabled 這個(gè)屬性的,改為 control.isEnabled 就可以了??磥?lái)蘋(píng)果的工程師做 PPT 有時(shí)候還是不太嚴(yán)謹(jǐn)。
另外,iOS SDK 中的 API 也用這個(gè)特性做了優(yōu)化,例如:
// Objective-C API @interface NSCandidateListTouchBarItem<CandidateType> : NSTouchBarItem @property (nullable, weak) NSView <NSTextInputClient> *client; @end
這個(gè) API 的 Objective-C 版本是沒(méi)有問(wèn)題的,可以知道 client 屬性既是一個(gè) NSView,又符合 NSTextInputClient 協(xié)議。然而它對(duì)應(yīng)的 Swift 3 版本為:
class NSCandidateListTouchBarItem<CandidateType: AnyObject> : NSTouchBarItem {
var client: NSView?
}
僅僅是一個(gè) NSView 類型 /(ㄒoㄒ)/~~
在 Swift 4 中,這類 API 做了優(yōu)化,改成了:
class NSCandidateListTouchBarItem<CandidateType: AnyObject> : NSTouchBarItem {
var client: (NSView & NSTextInputClient)?
}
這樣類型的聲明就更加嚴(yán)謹(jǐn)了。
Associated Type 可以追加 Where 約束語(yǔ)句
在 Swift 4 中可以在 associatedtype 后面聲明的類型后追加 where 語(yǔ)句
associatedtype Element where <xxx>
看下面是 Swift 4 標(biāo)準(zhǔn)庫(kù)中 Sequence 中 Element 的聲明:
protocol Sequence {
associatedtype Element where Self.Element == Self.Iterator.Element
// ...
}
它限定了 Sequence 中 Element 這個(gè)類型必須和 Iterator.Element 的類型一致。
通過(guò) where 語(yǔ)句可以對(duì)類型添加更多的約束,使其更嚴(yán)謹(jǐn),避免在使用這個(gè)類型時(shí)做多余的類型判斷。
新的 Key Paths 語(yǔ)法
先來(lái)看看 Swift 3 中 Key Paths 的寫(xiě)法:
@objcMembers class Kid: NSObject {
dynamic var nickname: String = ""
dynamic var age: Double = 0.0
dynamic var friends: [Kid] = []
}
var ben = Kid(nickname: "Benji", age: 5.5)
let kidsNameKeyPath = #keyPath(Kid.nickname)
let name = ben.valueForKeyPath(kidsNameKeyPath)
ben.setValue("Ben", forKeyPath: kidsNameKeyPath)
Swift 4 中創(chuàng)建一個(gè) KeyPath 用 `` 作為開(kāi)頭:
\Kid.nickname
當(dāng)編譯器可以推導(dǎo)出類型時(shí),可以省略基礎(chǔ)類型部分:
\.nickname
上面的代碼在 Swift 4 中就可以這樣寫(xiě):
struct Kid {
var nickname: String = ""
var age: Double = 0.0
var friends: [Kid] = []
}
var ben = Kid(nickname: "Benji", age: 8, friends: [])
let name = ben[keyPath: \Kid.nickname]
ben[keyPath: \Kid.nickname] = "BigBen"
相比 Swift 3,Swift 4 的 Key Paths 具有以下優(yōu)勢(shì):
- 類型可以定義為 class、struct
- 定義類型時(shí)無(wú)需加上 @objcMembers、dynamic 等關(guān)鍵字
- 性能更好
- 類型安全和類型推斷,例如 ben.valueForKeyPath(kidsNameKeyPath) 返回的類型是 Any,ben[keyPath: \Kid.nickname] 直接返回 String 類型
- 可以在所有值類型上使用
下標(biāo)支持泛型
有時(shí)候會(huì)寫(xiě)一些數(shù)據(jù)容器,Swift 支持通過(guò)下標(biāo)來(lái)讀寫(xiě)容器中的數(shù)據(jù),但是如果容器類中的數(shù)據(jù)類型定義為泛型,以前的下標(biāo)語(yǔ)法就只能返回 Any,在取出值后需要用 as? 來(lái)轉(zhuǎn)換類型。Swift 4 定義下標(biāo)也可以使用泛型了。
struct GenericDictionary<Key: Hashable, Value> {
private var data: [Key: Value]
init(data: [Key: Value]) {
self.data = data
}
subscript<T>(key: Key) -> T? {
return data[key] as? T
}
}
let dictionary = GenericDictionary(data: ["Name": "Xiaoming"])
let name: String? = dictionary["Name"] // 不需要再寫(xiě) as? String
二、字符串
Unicode 字符串在計(jì)算 count 時(shí)的正確性改善
在 Unicode 中,有些字符是由幾個(gè)其它字符組成的,比如 é 這個(gè)字符,它可以用 \u{E9} 來(lái)表示,也可以用 e 字符和上面一撇字符組合在一起表示 \u{65}\u{301}。
考慮以下代碼:
var family = "👩"
family += "\u{200D}👩"
family += "\u{200D}👧"
family += "\u{200D}👦"
print(family)
print(family.characters.count)
這個(gè) family 是一個(gè)由多個(gè)字符組合成的字符,打印出來(lái)的結(jié)果為 👩👩👧👦。上面的代碼在 Swift 3 中打印的 count 數(shù)是 4,在 Swift 4 中打印出的 count 是 1。
更快的字符處理速度
Swift 4 的字符串優(yōu)化了底層實(shí)現(xiàn),對(duì)于英語(yǔ)、法語(yǔ)、德語(yǔ)、西班牙語(yǔ)的處理速度提高了 3.5 倍。

對(duì)于簡(jiǎn)體中文、日語(yǔ)的處理速度提高了 2.5 倍。

去掉 characters
Swift 3 中的 String 需要通過(guò) characters 去調(diào)用的屬性方法,在 Swift 4 中可以通過(guò) String 對(duì)象本身直接調(diào)用,例如:
let values = "one,two,three..."
var i = values.characters.startIndex
while let comma = values.characters[i...<values.characters.endIndex].index(of: ",") {
if values.characters[i..<comma] == "two" {
print("found it!")
}
i = values.characters.index(after: comma)
}
Swift 4 可以把上面代碼中的所有的 characters 都去掉,修改如下:
let values = "one,two,three..."
var i = values.startIndex
while let comma = values[i...<values.endIndex].index(of: ",") {
if values[i..<comma] == "two" {
print("found it!")
}
i = values.index(after: comma)
}
One-sided Slicing
Swift 4 新增了一個(gè)語(yǔ)法糖 ... 可以對(duì)字符串進(jìn)行單側(cè)邊界取子串。
Swift 3:
let values = "abcdefg" let startSlicingIndex = values.index(values.startIndex, offsetBy: 3) let subvalues = values[startSlicingIndex..<values.endIndex] // defg
Swift 4:
let values = "abcdefg" let startSlicingIndex = values.index(values.startIndex, offsetBy: 3) let subvalues = values[startSlicingIndex...] // One-sided Slicing // defg
String 當(dāng)做 Collection 來(lái)用
Swift 4 中 String 可以當(dāng)做 Collection 來(lái)用,并不是因?yàn)?String 實(shí)現(xiàn)了 Collection 協(xié)議,而是 String 本身增加了很多 Collection 協(xié)議中的方法,使得 String 在使用時(shí)看上去就是個(gè) Collection。例如:
翻轉(zhuǎn)字符串:
let abc: String = "abc" print(String(abc.reversed())) // cba
遍歷字符:
let abc: String = "abc"
for c in abc {
print(c)
}
/*
a
b
c
*/
Map、Filter、Reduce:
// map
let abc: String = "abc"
_ = abc.map {
print($0.description)
}
// filter
let filtered = abc.filter { $0 == "b" }
// reduce
let result = abc.reduce("1") { (result, c) -> String in
print(result)
print(c)
return result + String(c)
}
print(result)
Substring

在 Swift 中,String 的背后有個(gè) Owner Object 來(lái)跟蹤和管理這個(gè) String,String 對(duì)象在內(nèi)存中的存儲(chǔ)由內(nèi)存其實(shí)地址、字符數(shù)、指向 Owner Object 指針組成。Owner Object 指針指向 Owner Object 對(duì)象,Owner Object 對(duì)象持有 String Buffer。當(dāng)對(duì) String 做取子字符串操作時(shí),子字符串的 Owner Object 指針會(huì)和原字符串指向同一個(gè)對(duì)象,因此子字符串的 Owner Object 會(huì)持有原 String 的 Buffer。當(dāng)原字符串銷毀時(shí),由于原字符串的 Buffer 被子字符串的 Owner Object 持有了,原字符串 Buffer 并不會(huì)釋放,造成極大的內(nèi)存浪費(fèi)。
在 Swift 4 中,做取子串操作的結(jié)果是一個(gè) Substring 類型,它無(wú)法直接賦值給需要 String 類型的地方。必須用 String(<substring>) 包一層,系統(tǒng)會(huì)通過(guò)復(fù)制創(chuàng)建出一個(gè)新的字符串對(duì)象,這樣原字符串在銷毀時(shí),原字符串的 Buffer 就可以完全釋放了。
let big = downloadHugeString() let small = extractTinyString(from: big) mainView.titleLabel.text = small // Swift 4 編譯報(bào)錯(cuò) mainView.titleLabel.text = String(small) // 編譯通過(guò)
多行字符串字面量
Swift 3 中寫(xiě)很長(zhǎng)的字符串只能寫(xiě)在一行。
func tellJoke(name: String, character: Character) {
let punchline = name.filter { $0 != character }
let n = name.count - punchline.count
let joke = "Q: Why does \(name) have \(n) \(character)'s in their name?\nA: I don't know, why does \(name) have \(n) \(character)'s in their name?\nQ: Because otherwise they'd be called \(punchline)."
print(joke)
}
tellJoke(name: "Edward Woodward", character: "d")
字符串中間有換行只能通過(guò)添加 \n 字符來(lái)代表?yè)Q行。
Swift 4 可以把字符串寫(xiě)在一對(duì) """ 中,這樣字符串就可以寫(xiě)成多行。
func tellJoke(name: String, character: Character) {
let punchline = name.filter { $0 != character }
let n = name.count - punchline.count
let joke = """
Q: Why does \(name) have \(n) \(character)'s in their name?
A: I don't know, why does \(name) have \(n) \(character)'s in their name?
Q: Because otherwise they'd be called \(punchline).
"""
print(joke)
}
tellJoke(name: "Edward Woodward", character: "d")
三、Swift 標(biāo)準(zhǔn)庫(kù)
Encoding and Decoding
當(dāng)需要將一個(gè)對(duì)象持久化時(shí),需要把這個(gè)對(duì)象序列化,往常的做法是實(shí)現(xiàn) NSCoding 協(xié)議,寫(xiě)過(guò)的人應(yīng)該都知道實(shí)現(xiàn) NSCoding 協(xié)議的代碼寫(xiě)起來(lái)很痛苦,尤其是當(dāng)屬性非常多的時(shí)候。幾年前有一個(gè)工具能自動(dòng)生成 Objective-C 的實(shí)現(xiàn) NSCoding 協(xié)議代碼,當(dāng)時(shí)用著還不錯(cuò),但后來(lái)這個(gè)工具已經(jīng)沒(méi)有人維護(hù)很久了,而且不支持 Swift。
Swift 4 中引入了 Codable 幫我們解決了這個(gè)問(wèn)題。
struct Language: Codable {
var name: String
var version: Int
}
我們想將這個(gè) Language 對(duì)象的實(shí)例持久化,只需要讓 Language 符合 Codable 協(xié)議即可,Language 中不用寫(xiě)別的代碼。符合了 Codable 協(xié)議以后,可以選擇把對(duì)象 encode 成 JSON 或者 PropertyList。
Encode 操作如下:
let swift = Language(name: "Swift", version: 4)
if let encoded = try? JSONEncoder().encode(swift) {
// 把 encoded 保存起來(lái)
}
Decode 操作如下:
if let decoded = try? JSONDecoder().decode(Language.self, from: encoded) {
print(decoded.name)
}
Sequence 改進(jìn)
Swift 3:
protocol Sequence {
associatedtype Iterator: IteratorProtocol
func makeIterator() -> Iterator
}
Swift 4:
protocol Sequence {
associatedtype Element
associatedtype Iterator: IteratorProtocol where Iterator.Element == Element
func makeIterator() -> Iterator
}
由于 Swift 4 中的 associatedtype 支持追加 where 語(yǔ)句,所以 Sequence 做了這樣的改進(jìn)。
Swift 4 中獲取 Sequence 的元素類型可以不用 Iterator.Element,而是直接取 Element。
SubSequence 也做了修改:
protocol Sequence {
associatedtype SubSequence: Sequence
where SubSequence.SubSequence == SubSequence,
SubSequence.Element == Element
}
通過(guò) where 語(yǔ)句的限定,保證了類型正確,避免在使用 Sequence 時(shí)做一些不必要的類型判斷。
Collection 也有一些類似的修改。
Protocol-oriented integers
整數(shù)類型符合的協(xié)議有修改,新增了 FixedWidthInteger 等協(xié)議,具體的協(xié)議繼承關(guān)系如下:
+-------------+ +-------------+
+------>+ Numeric | | Comparable |
| | (+,-,*) | | (==,<,>,...)|
| +------------++ +---+---------+
| ^ ^
+-------+------------+ | |
| SignedNumeric | +-+-------+-----------+
| (unary -) | | BinaryInteger |
+------+-------------+ |(words,%,bitwise,...)|
^ ++---+-----+----------+
| +-----------^ ^ ^---------------+
| | | |
+------+---------++ +---------+---------------+ +--+----------------+
| SignedInteger | | FixedWidthInteger | | UnsignedInteger |
| | |(endianness,overflow,...)| | |
+---------------+-+ +-+--------------------+--+ +-+-----------------+
^ ^ ^ ^
| | | |
| | | |
++--------+-+ +-+-------+-+
|Int family |-+ |UInt family|-+
+-----------+ | +-----------+ |
+-----------+ +-----------+
Dictionary and Set enhancements
這里簡(jiǎn)單列一下 Dictionary 和 Set 增強(qiáng)了哪些功能:
- 通過(guò) Sequence 來(lái)初始化
- 可以包含重復(fù)的 Key
- Filter 的結(jié)果的類型和原類型一致
- Dictionary 的 mapValues 方法
- Dictionary 的默認(rèn)值
- Dictionary 可以分組
- Dictionary 可以翻轉(zhuǎn)
NSNumber bridging and Numeric types
let n = NSNumber(value: 999) let v = n as? UInt8 // Swift 4: nil, Swift 3: 231
在 Swift 4 中,把一個(gè)值為 999 的 NSNumber 轉(zhuǎn)換為 UInt8 后,能正確的返回 nil,而在 Swift 3 中會(huì)不可預(yù)料的返回 231。
MutableCollection.swapAt(::)
MutableCollection 現(xiàn)在有了一個(gè)新方法 swapAt(::) 用來(lái)交換兩個(gè)位置的值,例如:
var mutableArray = [1, 2, 3, 4] mutableArray.swapAt(1, 2) print(mutableArray) // 打印結(jié)果:[1, 3, 2, 4]
四、構(gòu)建過(guò)程改進(jìn)
New Build System
Xcode 9 引入了 New Build System,可在 Xcode 9 的 File -> Project Settings... 中選擇開(kāi)啟。

預(yù)編譯 Bridging Headers 文件
對(duì)于 Swift 和 Objective-C 混合的項(xiàng)目,Swift 調(diào)用 Objective-C 時(shí),需要建立一個(gè) Bridging Headers 文件,然后把 Swift 要調(diào)用的 Objective-C 類的頭文件都寫(xiě)在里面,編譯器會(huì)讀取 Bridging Headers 中的頭文件,然后生成一個(gè)龐大的 Swift 文件,文件內(nèi)容是這些頭文件內(nèi)的 API 的 Swift 版本。然后編譯器會(huì)在編譯每一個(gè) Swift 文件時(shí),都要編譯一遍這個(gè)龐大的 Swift 文件的內(nèi)容。
有了預(yù)編譯 Bridging Headers 以后,編譯器會(huì)在預(yù)編譯階段把 Bridging Headers 編譯一次,然后插入到每個(gè) Swift 文件中,這樣就大大提高了編譯速度。
蘋(píng)果宣稱 Xcode 9 和 Swift 4 對(duì)于 Swift 和 Objective-C 混合編譯的速度提高了 40%。
Indexing 可以在編譯的同時(shí)進(jìn)行
用 Swift 開(kāi)發(fā)項(xiàng)目時(shí),近幾個(gè)版本的 Xcode 進(jìn)行 Indexing 的速度慢的令人發(fā)指。Xcode 9 和 Swift 4 在這方面做了優(yōu)化,可以在編譯的同時(shí)進(jìn)行 Indexing,一般編譯結(jié)束后 Indexing 也會(huì)同時(shí)完成。
COW Existential Containers
Swift 中有個(gè)東西叫 Existential Containers,它用來(lái)保存未知類型的值,它的內(nèi)部是一個(gè) Inline value buffer,如果 Inline value buffer 中的值占用空間很大時(shí),這個(gè)值會(huì)被分配在堆上,然而在堆上分配內(nèi)存是一個(gè)性能比較慢的操作。
Swift 4 中為了優(yōu)化性能引入了 COW Existential Containers,這里的 COW 就代表 "Copy-On-Write",當(dāng)存在多個(gè)相同的值時(shí),他們會(huì)共用 buffer 上的空間,直到某個(gè)值被修改時(shí),這個(gè)被修改的值才會(huì)被拷貝一份并分配內(nèi)存空間。
移除未調(diào)用的協(xié)議實(shí)現(xiàn)
struct Date {
private let secondsSinceReferenceDate: Double
}
extension Date: Equatable {
static func ==(lhs: Date, rhs: Date) -> Bool {
return lhs.secondsSinceReferenceDate == rhs.secondsSinceReferenceDate
}
}
extension Date: Comparable {
static func <(lhs: Date, rhs: Date) -> Bool {
return lhs.secondsSinceReferenceDate < rhs.secondsSinceReferenceDate
}
}
看上面例子,Date 實(shí)現(xiàn)了 Equatable 和 Comparable 協(xié)議。編譯時(shí)如果編譯器發(fā)現(xiàn)沒(méi)有任何地方調(diào)用了對(duì) Date 進(jìn)行大小比較的方法,編譯器會(huì)移除 Comparable 協(xié)議的實(shí)現(xiàn),來(lái)達(dá)到減小包大小的目的。
減少隱式 @objc 自動(dòng)推斷
在項(xiàng)目中想把 Swift 寫(xiě)的 API 暴露給 Objective-C 調(diào)用,需要增加 @objc。在 Swift 3 中,編譯器會(huì)在很多地方為我們隱式的加上 @objc,例如當(dāng)一個(gè)類繼承于 NSObject,那么這個(gè)類的所有方法都會(huì)被隱式的加上 @objc。
class MyClass: NSObject {
func print() { ... } // 包含隱式的 @objc
func show() { ... } // 包含隱式的 @objc
}
這樣很多并不需要暴露給 Objective-C 也被加上了 @objc。大量 @objc 會(huì)導(dǎo)致二進(jìn)制文件大小的增加。
在 Swift 4 中,隱式 @objc 自動(dòng)推斷只會(huì)發(fā)生在很少的當(dāng)必須要使用 @objc 的情況,比如:
- 復(fù)寫(xiě)父類的 Objective-C 方法
- 符合一個(gè) Objective-C 的協(xié)議
其它大多數(shù)地方必須手工顯示的加上 @objc。
減少了隱式 @objc 自動(dòng)推斷后,Apple Music app 的包大小減少了 5.7%。
五、 Exclusive Access to Memory
在遍歷一個(gè) Collection 的時(shí)候可以去修改每一個(gè)元素的值,但是在遍歷時(shí)如果去添加或刪除一個(gè)元素就可能會(huì)引起 Crash。
例如為 MutableCollection 擴(kuò)展一個(gè) modifyEach 方法來(lái)修改每個(gè)元素的值,代碼如下:
extension MutableCollection {
mutating func modifyEach(_ body: (inout Element) -> ()) {
for index in self.indices {
body(&self[index])
}
}
}
假如在調(diào)用 modifyEach 時(shí)去刪除元素:
var numbers = [1, 2, 3]
numbers.modifyEach { element in
element *= 2
numbers.removeAll()
}
就會(huì)在運(yùn)行時(shí) Crash。Swift 4 中引入了 Exclusive Access to Memory,使得這個(gè)錯(cuò)誤可以在編譯時(shí)被檢查出來(lái)。
六、 兼容性
Xcode 9 中同時(shí)集成了 Swift 3.2 和 Swift 4。
- Swift 3.2 完全兼容 Swift 3.1,并會(huì)在過(guò)時(shí)的語(yǔ)法或函數(shù)上報(bào)告警告。
- Swift 3.2 具有 Swift 4 的一些寫(xiě)法,但是性能不如 Swift 4。
- Swift 3.2 和 Swift 4 可以混合編譯,可以指定一部分模塊用 Swift 3.2 編譯,一部分用 Swift 4 編譯。
- 遷移到 Swift 4 后能獲得 Swift 4 所有的新特性,并且性能比 Swift 3.2 好。
總結(jié):當(dāng) Xcode 正式版發(fā)布后,現(xiàn)有的 Swift 代碼可以直接升級(jí)到 Swift 3.2 而不用做任何改動(dòng),后續(xù)可以再遷移到 Swift 4?;蛘咧苯舆w移到 Swift 4 也可以,Swift 4 相比 Swift 3 的 API 變化還是不大的,很多第三方庫(kù)都可以直接用 Swift 4 編譯。Swift 1 到 2 和 Swift 2 到 3 的遷移的痛苦在 3 到 4 的遷移上已經(jīng)大大改善了。
七、參考資料
- WWDC 2017 Session 402 《What's New in Swift》
- WWDC 2017 Session 212 《What's New in Foundation》
- WWDC 2017 Session 102 《Platforms State of the Union》
- 《Swift Language Programming (Swift 4.0)》
- https://github.com/apple/swift-evolution
- https://github.com/ole/whats-new-in-swift-4
- https://www.raywenderlich.com/163857/whats-new-swift-4
- https://www.hackingwithswift.com/swift4
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
RxSwift發(fā)送及訂閱 Subjects、Variables代碼示例
這篇文章主要介紹了RxSwift發(fā)送及訂閱 Subjects、Variables代碼示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-12-12
Swift使用SnapKit模仿Kingfisher第三方擴(kuò)展優(yōu)化
這篇文章主要為大家介紹了Swift?SnapKit模仿Kingfisher第三方擴(kuò)展優(yōu)化示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
Spring中BeanFactory與FactoryBean的區(qū)別解讀
這篇文章主要介紹了Spring中BeanFactory與FactoryBean的區(qū)別解讀,Java的BeanFactory是Spring框架中的一個(gè)接口,它是用來(lái)管理和創(chuàng)建對(duì)象的工廠接口,在Spring中,我們可以定義多個(gè)BeanFactory來(lái)管理不同的組件,需要的朋友可以參考下2023-12-12
Swift實(shí)現(xiàn)簡(jiǎn)單計(jì)算器
這篇文章主要為大家詳細(xì)介紹了Swift實(shí)現(xiàn)簡(jiǎn)單計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
Swift網(wǎng)絡(luò)請(qǐng)求庫(kù)Alamofire使用詳解
這篇文章主要為大家詳細(xì)介紹了Swift網(wǎng)絡(luò)請(qǐng)求庫(kù)Alamofire的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08

