swift4.0新特性的記錄

最新在寫swift的時(shí)候感覺不怎么順手,于是把新特性看了一遍,順便做一下筆記,加深一下記憶。

1、語法改進(jìn)

extesion 中可以訪問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實(shí)現(xiàn)Equatable,Comparable的協(xié)議。為了更swift話一下,可以將代碼改造成。


struct Date {
    private let secondsSinceReferenceDate: Double // 結(jié)構(gòu)體中定一個(gè)私有屬性
}

// 擴(kuò)展結(jié)構(gòu)體,實(shí)現(xiàn)Equatable,Equatable協(xié)議
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
    }
}

swift4能這樣調(diào)用,但是swift3將會(huì)提示錯(cuò)誤,要把Date中的private改成fileprivate,這樣會(huì)把作用域變大,造成屬性的濫用。而swift4,就是將private的作用域變大,擴(kuò)展到extension中,這樣就不必更改屬性的作用域了。

類型+協(xié)議 生成的組合類型

protocol Shakeable {
    func shake()
}

extension UIButton: Shakeable {
    func shake() {
        print("a")
    }
}
extension UISlider: Shakeable {
    func shake() {
        print("b")
    }
}

func shakeEm(controls: [UIControl & Shakeable]) {
    for control in controls where control.isEnabled {
        control.shake()
    }
}

這樣就可以直接使用類型中遵循協(xié)議的方法,而不必要再判斷cotrol是否遵循這個(gè)協(xié)議,然后在as一下來調(diào)用協(xié)議中的方法。

associatedtype 可以追加where的約束

protocol Shakeable {
    associatedtype Element where Self.Element == Self.Iterator.Element
    func shake()
}

這樣可以避免在使用Shakeable時(shí)多做一個(gè)類型判斷

key paths 語法的改變

因?yàn)橐郧暗膶懛?,這個(gè)在swift4上總是寫的不舒服。swift4創(chuàng)建一個(gè)keyPath要用\開頭

\keyPath

swift3使用setValue來寫

@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)

而變成swift4就用了

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"

不是很習(xí)慣。
swift4的keyPath新特點(diǎn):
1、可以定義class、struct,而不必加上@objcMembers、dynamic的關(guān)鍵字
2、類型安全和類型推斷
3、可以用在所有的值類型上

下標(biāo)支持泛型

現(xiàn)在下標(biāo)用泛型不需要用as來轉(zhuǎn)換類型了
例:

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" : "xx"])
let name: String? = dictionary["name"]

字符串

Unicode字符串在計(jì)算count時(shí)的正確性改善

var family = "??"
print(family.characters.count)  // 1

去掉characters

現(xiàn)在string的某些屬性可以不用characters。但是自動(dòng)填充下有些加的characters的類型和不加的類型有些不同。

可以取單側(cè)邊界

新加語法糖...可以對(duì)字符串取單側(cè)邊界

let v = "hhgggdads"
let startSlicIndex = v.index(v.startIndex, offsetBy: 3)
let subvalue = v[startSlicIndex...]

String實(shí)現(xiàn)了Collection協(xié)議

因?yàn)閷?shí)現(xiàn)了collection協(xié)議,所以現(xiàn)在可以調(diào)用collection 的方法
例:

//翻轉(zhuǎn)字符串
let abc: String = "abc"
print(String(abc.reversed()))

//遍歷
for i in abc {
    print(i)
}

//map
_ = abc.map {
    print($0.description)
}

//filter
let filterd = 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 來跟蹤和管理這個(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 類型,它無法直接賦值給需要 String 類型的地方。必須用 String() 包一層,系統(tǒng)會(huì)通過復(fù)制創(chuàng)建出一個(gè)新的字符串對(duì)象,這樣原字符串在銷毀時(shí),原字符串的 Buffer 就可以完全釋放了。

多行字符串字面量

swift4可以把字符串寫在一對(duì)"""中,這樣字符串就可以寫成多行。

let joke = """
Q: Why does  have  in their name?
A: I don't know, why does  have s in their name?
Q: Because otherwise they'd be called (punchline).
"""

標(biāo)準(zhǔn)庫

Encoding and Decoding

swift4中引入Codable來解決對(duì)象持久話問題

struct Language: Codable {
    var name: String
    var version: Int
}

將language對(duì)象的實(shí)例持久化,只要language遵循Codable協(xié)議就好,然后encode成json或者PropertyList

//encode
let swift = Language(name:"swift",version: 4)
if let encode = try? JSONEncoder().encode(swift){
    // 保存encode

}

//decode
if let decoded = try? JSONDecoder().decode(Language.self, from: encoded) {
    print(decoded.name)
}

sequence的改進(jìn)

protocol Sequence {
    associatedtype Element
    associatedtype Iterator: IteratorProtocol where Iterator.Element == Element
    func makeIterator() -> Iterator
}

swift4的associatedtype支持追加where語句,這樣獲取Sequence的元素類型可以不用 Iterator.Element,而是直接取 Element。

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|-+
               +-----------+ |              +-----------+ |
                 +-----------+                +-----------+

ps:個(gè)人也不是很理解,只是參看了文檔上的解釋。

Dictionary and Set enhancements

dictionary和set增強(qiáng)的功能:
1、通過Sequence來初始化
2、可以包含重復(fù)的Key
3、Filter的結(jié)果的類型和原類型一致
4、dictionary的mapValues方法
5、dictionary的默認(rèn)值
6、dictionary可以分組
7、dictionary可以翻轉(zhuǎn)

NSNumber bridging and Numeric types

let n = NSNumber(value: 999)
let v = n as? UInt8 // Swift 4: nil, Swift 3: 231

swift4中,把值為999的NSNumber轉(zhuǎn)換為UInt8后會(huì)返回nil,而swift3會(huì)返回231這個(gè)意外的值

MutableCollection.swapAt(::)

MutableCollection 現(xiàn)在有了一個(gè)新方法 swapAt(::) 用來交換兩個(gè)位置的值

var mutableArray = [1, 2, 3, 4]
mutableArray.swapAt(1, 2)
print(mutableArray)
// 打印結(jié)果:[1, 3, 2, 4]

工程上

預(yù)編譯 Bridging Headers 文件

編譯器會(huì)預(yù)編譯 Bridging Headers 文件,這樣就不要在編譯的時(shí)候每次都編譯oc的頭文件生成的swift文件,從加快了編譯速度。

Indexing 可以在編譯的同時(shí)進(jìn)行

用 Swift 開發(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,它用來保存未知類型的值,它的內(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)沒有任何地方調(diào)用了對(duì) Date 進(jìn)行大小比較的方法,編譯器會(huì)移除 Comparable 協(xié)議的實(shí)現(xiàn),來達(dá)到減小包大小的目的。

減少隱式 @objc 自動(dòng)推斷

在 Swift 4 中,隱式 @objc 自動(dòng)推斷只會(huì)發(fā)生在很少的當(dāng)必須要使用 @objc 的情況,比如:

復(fù)寫父類的 Objective-C 方法
符合一個(gè) Objective-C 的協(xié)議
其它大多數(shù)地方必須手工顯示的加上 @objc。

減少了隱式 @objc 自動(dòng)推斷后,Apple Music app 的包大小減少了 5.7%。

OK筆記就到這,下面是參考資料:

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容