What's new in Swift 4 - Swift 4 資料整理

Swift 4.0 出來了,這幾天也是重點關注了下Swift 4.0的新特性奏窑,看了WWDC中關于Swift部分的視頻些椒,也研究了下github上一個Swift 4.0新特性的開源項目whats-new-in-swift-4扶歪,很全面特占,有解釋有代碼糙置。自己也整理了一下,主要是為了加深理解是目。
整體來看谤饭,Swift 4.0的語法變動并不大,從Swift 3 升級到Swift 4并不麻煩懊纳,更多的還是性能方面的提升揉抵,比如對Strings的底層優(yōu)化,對@objc的優(yōu)化嗤疯,以及Xcode9上的性能提升冤今。

One-sided ranges 單側邊界

在某些情況下可以省略startIndex或endIndex

  • Used in Collection subscripts 集合下標操作
let letters = ["a", "b", "c", "d"]
let sub1 = letters[2..<letters.endIndex] // ["c", "d"]
let sub2 = letters[2…]                   // ["c", "d”]
let sub3 = letters[..<1]                 // ["a"]
  • Used in Pattern Matching 模式匹配
let value = 5
switch value {
case 1...:
    print("greater than zero")
case 0:
    print("zero")
case ..<0:
    print("less than zero")
default:
    fatalError("unreachable")
}

結果是打印greater than zero

Strings 字符串

  • 性能提升
    Swift 4 的字符串優(yōu)化了底層實現(xiàn),對于英語身弊、法語、德語列敲、西班牙語的處理速度提高了 3.5 倍阱佛,對于簡體中文、日語的處理速度提高了 2.5 倍
  • Multi-line string literals 多行字符串字面量
let multilineString = """
This is a multi-line string. //這里實際上會被加入一個\n
You don't have to escape "quotes" in here.
The position of the closing delimiter
controls whitespace stripping.
"""
  • remove Characters 去掉了characters
let greeting = "Hello, World!"
greeting.count  // Swift3: greeting.characters.count
  • String is a Collection 集合
    String本身沒有實現(xiàn)Collection協(xié)議戴而,只是增加了很多Collection協(xié)議的方法凑术,使得它可以被當做Collection來使用
for char in greeting {
    print(char)
}
greeting.filter { …. }
greeting.map { …. }
  • Substring is the new type for string slices 新增類型Substring
    對String進行substring等操作會得到一個類型為Substring的字符串。Substring跟String很類似所意,他們都實現(xiàn)了StringProtocol淮逊。
let comma = greeting.index(of: ",")!
let substring = greeting[..<comma]
type(of: substring) //Substring.Type
print(substring.uppercased()) // String API can be called on Substring

之所以重新定義了一個Substring催首,主要是為了提高性能。
在 Swift 中泄鹏,String 的背后有個 Owner Object 來跟蹤和管理這個 String郎任,String 對象在內存中的存儲由內存起始地址、字符數(shù)备籽、指向 Owner Object 指針組成舶治。Owner Object 指針指向 Owner Object 對象,Owner Object 對象持有 String Buffer车猬。當對 String 做取子字符串操作時霉猛,子字符串的 Owner Object 指針會和原字符串指向同一個對象,因此子字符串的 Owner Object 會持有原 String 的 Buffer珠闰。當原字符串銷毀時惜浅,由于原字符串的 Buffer 被子字符串的 Owner Object 持有了,原字符串 Buffer 并不會釋放伏嗜,造成極大的內存浪費坛悉。
在 Swift 4 中,做取子串操作的結果是一個 Substring 類型阅仔,它無法直接賦值給需要 String 類型的地方吹散。必須用 String(<substring>) 包一層,系統(tǒng)會通過復制創(chuàng)建出一個新的字符串對象八酒,這樣原字符串在銷毀時空民,原字符串的 Buffer 就可以完全釋放了。

  • Unicode
    改善了在計算Unicode字符長度時的正確性
    在 Unicode 中羞迷,有些字符是由幾個其它字符組成的界轩,比如 é 這個字符,它可以用 \u{E9} 來表示
var family = "??"      // "??"
family += "\u{200D}??” // "?????"
family += "\u{200D}??” //"????????"
family += "\u{200D}??” //"???????????"
family.count          //在swift3中衔瓮,count=4浊猾,在swift4中,count=1

Private declarations visible in same-file extensions 修改了Private修飾符的權限范圍

private修飾的屬性或方法热鞍,可以在同文件中的extension中訪問到葫慎。跟swift3中的fileprivate相似而又不同。相同點是都可以在同一個文件中訪問到薇宠,不同點是private修飾的只能在當前類型的extension中訪問到偷办,而fileprivate修飾的,也可以在其他的類型定義和extension中訪問到澄港。

struct SortedArray<Element: Comparable> {
    private var storage: [Element] = []
    init(unsorted: [Element]) {
        storage = unsorted.sorted()
    }
}
extension SortedArray {
    mutating func insert(_ element: Element) {
        // storage is visible here
        storage.append(element)
        storage.sort()
    }
}
let array = SortedArray(unsorted: [3,1,2])
// 這里無法訪問到storage屬性(跟fileprivate不同)
//array.storage // error: 'storage' is inaccessible due to 'private' protection level

Key Paths

類似Objective-C里的KVC椒涯,Swift 4.0里的Key Paths是強類型的

struct Person {
    var name: String
}
struct Book {
    var title: String
    var authors: [Person]
    var primaryAuthor: Person {
        return authors.first!
    }
}
let abelson = Person(name: "Harold Abelson")
let sussman = Person(name: "Gerald Jay Sussman")
var sicp = Book(title: "Structure and Interpretation of Computer Programs", authors: [abelson, sussman])
  • 使用key paths獲取對象值
sicp[keyPath: \Book.title] 
//Structure and Interpretation of Computer Programs
  • key paths 可以用于計算屬性
 sicp[keyPath: \Book.primaryAuthor.name] 
//Harold Abelson
  • 使用key paths可以修改值
sicp[keyPath: \Book.title] = "Swift 4.0” 
//sicp.title 現(xiàn)在是Swift 4.0
  • key paths是對象,可以被存儲和操縱
let authorKeyPath = \Book.primaryAuthor
print(type(of: authorKeyPath)) //KeyPath<Book, Person>
let nameKeyPath = authorKeyPath.appending(path: \.name) // 這個可以省略Book回梧,因為編譯器可以推斷出類型
sicp[keyPath: nameKeyPath] //Harold Abelson

KeyPath實際上是一個class废岂,它的定義如下:

/// A key path from a specific root type to a specific resulting value type.
public class KeyPath<Root, Value> : PartialKeyPath<Root> {
}
  • key paths暫時還不支持下標操縱
//sicp[keyPath: \Book.authors[0].name] //編譯失敗

Archival and serialization 歸檔和序列化

可以通過指定Swift 類型(class, enum, struct)實現(xiàn)Codable協(xié)議來標識該類型支持歸檔和序列化祖搓。
大部分情況下,如果某個類型的所有成員類型都實現(xiàn)了Codeable協(xié)議的話湖苞,只需要這一步就可以完成對該類型的數(shù)據的歸檔和序列化功能的支持拯欧,因為編譯器會自動生成相應的encode\decode方法,當然你也可以重寫這些方法袒啼。
Codable其實是一個組合協(xié)議:

public typealias Codable = Decodable & Encodable 
struct Card: Codable {
    enum Suit: String, Codable {
        case clubs, spades, hearts, diamonds
    }
    enum Rank: Int, Codable {
        case ace = 1, two, three, four, five, six, seven, eight, nine, ten, jack, queen, king
    }
    var suit: Suit
    var rank: Rank
}
let hand = [Card(suit: .clubs, rank: .ace), Card(suit: .hearts, rank: .queen)]
  • encode
//json
var encoder = JSONEncoder()
let jsonData = try encoder.encode(hand)
String(data: jsonData, encoding: .utf8) 

結果是:

[{\"rank\":1,\"suit\":\"clubs\"},{\"rank\":12,\"suit\":\"hearts\”}]
//plist
var encoder2 = PropertyListEncoder()
encoder2.outputFormat = .xml
let propertyData = try encoder2.encode(hand)
String(data: propertyData, encoding: .utf8)

結果是:

//<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<array>\n\t<dict>\n\t\t<key>rank</key>\n\t\t<integer>1</integer>\n\t\t<key>suit</key>\n\t\t<string>clubs</string>\n\t</dict>\n\t<dict>\n\t\t<key>rank</key>\n\t\t<integer>12</integer>\n\t\t<key>suit</key>\n\t\t<string>hearts</string>\n\t</dict>\n</array>\n</plist>
  • decode
//json
var decoder = JSONDecoder()
let hand2 = try decoder.decode([Card].self, from: jsonData)  //[{clubs, ace}, {hearts, queen}]
hand2[0].suit //clubs
//plist
var decoder2 = PropertyListDecoder()
let hand3 = try decoder2.decode([Card].self, from: propertyData)
hand3[0].suit //clubs

Associated type constraints 關聯(lián)類型約束

  • protocol中也可以使用where語句對關聯(lián)類型進行約束了
protocol SomeProtocol where Self: UICollectionViewCell {
}

SomeProtocol要求它的實現(xiàn)者必須繼承UICollectionViewCell哈扮,不是隨便一個類型都能實現(xiàn)SomeProtocol

  • Sequence協(xié)議有自己的Element associatedtype了,不需要寫Iterator.Element
extension Sequence where Element: Numeric {
    var sum: Element {
        var result: Element = 0
        for element in self {
            result += element
        }
        return result
    }
}
[1,2,3,4].sum

Dictionary and Set enhancements 加強

  • 使用key-value sequence初始化Dictionary
let names = ["Cagney", "Lacey", "Bensen”]
let dict = Dictionary(uniqueKeysWithValues: zip(1..., names))
//[2: "Lacey", 3: "Bensen", 1: "Cagney”]

如果key存在重復的情況蚓再,使用:

let users = [(1, "Cagney"), (2, "Lacey"), (1, "Bensen”)] 
//zip函數(shù)的作用就是把兩個Sequence合并成一個key-value元組的Sequence
let dict = Dictionary(users, uniquingKeysWith: {
    (first, second) in
    print(first, second)
    return first
})
//[2: "Lacey", 1: "Cagney”]
  • merge 合并
let defaults = ["foo": false, "bar": false, "baz": false]
var options = ["foo": true, "bar": false]
// 按照merge函數(shù)的注釋應該是如下寫法滑肉,但是這種寫法會報錯error: generic parameter 'S' could not be inferred
//let mergedOptions = options.merge(defaults) { (old, _) in old }
// 需要替換成
options.merge(defaults.map { $0 }) { (old, _) in old }options
//["bar": false, "baz": false, "foo": true]
  • 帶默認值的下標操作
    使用下標取某個key值時,可以傳一個default參數(shù)作為默認值摘仅,當這個key不存在時靶庙,會返回這個默認值
let source = "how now brown cow"
var frequencies: [Character: Int] = [:]
for c in source {
    frequencies[c, default: 0] += 1
}
print(frequencies)
//["b": 1, "w": 4, "r": 1, "c": 1, "n": 2, "o": 4, " ": 3, "h": 1]
  • map和filter函數(shù)返回值類型是Dictionary,而不是Array
let filtered = dict.filter {
    $0.key % 2 == 0
}
filtered //[2: "Lacey"]
let mapped = dict.mapValues { value in
    value.uppercased()
}
mapped //[2: "LACEY", 1: "CAGNEY”]
  • 對Sequence進行分組
let contacts = ["Julia", "Susan", "John", "Alice", "Alex"]
let grouped = Dictionary(grouping: contacts, by: { $0.first! })
print(grouped)
//["J": ["Julia", "John"], "S": ["Susan"], "A": ["Alice", "Alex"]]

MutableCollection.swapAt method

新增的一個方法娃属,用于交換MutableCollection中的兩個位置的元素

var numbers = [1,2,3,4,5]
numbers.swapAt(0,1) //[2, 1, 3, 4, 5]

Generic subscripts 泛型下標

下標操作現(xiàn)在支持傳遞泛型參數(shù)和返回值類型了

struct JSON {
    fileprivate var storage: [String:Any]

    init(dictionary: [String:Any]) {
        self.storage = dictionary
    }
    subscript<T>(key: String) -> T? {
        return storage[key] as? T
    }
}
let json = JSON(dictionary: [
    "name": "Berlin",
    "country": "de",
    "population": 3_500_500
    ])

// No need to use as? Int
let population: Int? = json["population"]

NSNumber bridging 橋接

主要修復了幾個NSNumber bridging的問題

let n = NSNumber(value: UInt32(543))
let v = n as? Int8 // nil in Swift 4. This would be 31 in Swift 3 (try it!).

Composing classes and protocols

協(xié)議與協(xié)議可以組合六荒,協(xié)議與class也可以組合

protocol Shakeable {
    func shake()
}

func shakeControls(_ controls:[Shakeable & UIControl]) {
    for control in controls {
        if control.isEnabled {
            control.shake()
        }
    }
}

從 Swift 3 升級到 Swift 4

由于公司項目中就有Swift開發(fā)的App,因此看完Swift 4.0的新特性后矾端,必須得來嘗試一下掏击,看看到底有多少個坑。下面主要是我目前遇到的一些問題:

  • 作為#selector函數(shù)參數(shù)的方法必須添加@objc標識秩铆,可根據Xcode提示自動修復
  • dynamic修飾的屬性必須添加@objc標識砚亭,可根據Xcode提示自動修復`
  • 需要暴露給OC調用的屬性或方法必須添加@objc標識
    這個坑有時候隱藏的比較深,有時候只有在運行時才會出現(xiàn)殴玛,而一旦出現(xiàn)捅膘,就會Crash,如果項目中有混編的滚粟,一定要小心寻仗。
  • 一些字符串常量修改,可根據Xcode提示自動修復
NSFontAttributeName -> NSAttributedStringKey.font
NSForegroundColorAttributeName -> NSAttributedStringKey.foregroundColor
  • extension中的函數(shù)不能被override了凡壤,兩種解決辦法:
    1.把func移到class的申明里
    2.在extension的func前面加上@objc dynamic標識

其中署尤,對于@objc的使用要比之前多了不少,這個改動主要是為了給編譯生成的二進制文件進行瘦身的亚侠。
在之前的Swift 版本中曹体,所有繼承自NSObject的類的屬性和方法都會被默認加上@objc標識,以便將其暴露給Objective-C盖奈,編譯器會為這些方法和屬性生成可供OC調用的代碼混坞,但是實際情況可能只有很少量的幾個方法需要暴露給OC狐援,這就造成很大的空間浪費钢坦。因此在Swift 4中究孕,編譯器大部分情況下不會自動添加@objc標識,如果要暴露給OC爹凹,需要手動添加厨诸。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市禾酱,隨后出現(xiàn)的幾起案子微酬,更是在濱河造成了極大的恐慌,老刑警劉巖颤陶,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件颗管,死亡現(xiàn)場離奇詭異,居然都是意外死亡滓走,警方通過查閱死者的電腦和手機垦江,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來搅方,“玉大人比吭,你說我怎么就攤上這事∫涛校” “怎么了衩藤?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長涛漂。 經常有香客問我赏表,道長,這世上最難降的妖魔是什么怖喻? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任底哗,我火速辦了婚禮,結果婚禮上锚沸,老公的妹妹穿的比我還像新娘跋选。我一直安慰自己,他們只是感情好哗蜈,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布前标。 她就那樣靜靜地躺著,像睡著了一般距潘。 火紅的嫁衣襯著肌膚如雪炼列。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天音比,我揣著相機與錄音俭尖,去河邊找鬼。 笑死,一個胖子當著我的面吹牛稽犁,可吹牛的內容都是我干的焰望。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼已亥,長吁一口氣:“原來是場噩夢啊……” “哼熊赖!你這毒婦竟也來了?” 一聲冷哼從身側響起虑椎,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤震鹉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后捆姜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體传趾,經...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年泥技,在試婚紗的時候發(fā)現(xiàn)自己被綠了墨缘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡零抬,死狀恐怖镊讼,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情平夜,我是刑警寧澤蝶棋,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站忽妒,受9級特大地震影響玩裙,放射性物質發(fā)生泄漏。R本人自食惡果不足惜段直,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一吃溅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鸯檬,春花似錦决侈、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至功茴,卻和暖如春庐冯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背坎穿。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工展父, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留返劲,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓栖茉,卻偏偏與公主長得像旭等,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子衡载,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內容