【譯】Swift 3.0更新概要

原貼:What's New in Swift?
做者:Ben Morrow
譯者: kemchenjhtml

Swift 3.0年底就會發布正式版, 而且會給全部Swift開發者帶來不少改變.ios

若是你沒有一直緊跟Swift Evolution的話, 你也許會想知道都有哪些改變, 以及它將會怎麼影響你的代碼, 而且你該何時開始着手把代碼convert到3.0, 那這篇文章就是寫給你的.git

這篇文章裏, 我將會着筆於那些對你的代碼會有重大影響的Swift 3的新特性. 開始吧!程序員

引言

Swift 3.0預覽版在Xcode 8.0 beta已經可使用了. 當Swift 3.0的改革已經接近尾聲的時候, 接下來的幾個月也許還會再接受幾個提案, 功能性修改將會在2016年年底xcode發佈正式版以後中止, 因此你必須等到那個時候才能把你的Swift 3應用提交到App Storegithub

爲了讓開發者可以開始着手移植到Swift 3.0, Apple已經把Swift 2.3內置到Xcode 8.0裏. 對開發者來講, Swift 2.3跟2.2同樣, 但2.3支持WWDC上宣佈的那些新的SDK和Xcode的新功能, Xcode 8.0出來以後, 你就能夠提交使用Swift 2.3寫的app, 而不用等到3.0才能體驗到那些新的特性.objective-c

我建議嘗試一下咱們在playground實現的功能, 甚至能夠在你的某個項目裏跑一下Migration Assistant感覺一下那些改變. 但Xcode 8.0和Swift 3.0還沒出正式版以前, 你還不能夠提交app到App sSore裏, 你也許須要考慮一下等Swift 3安定下來以後再把本身的全部代碼移植過去.編程

Swift 3的遷移工做

轉到Swift 3.0的時候, 你必須意識到全部的文件都須要作修改! 這麼大的改變是由於Cocoa的API名字都改了. 或者爲了更加明確, API會保持原樣, 但會有一個合適的名字給Objective-C和另外一個給Swift. 接下里的幾年裏, Swift 3.0將會讓Swift變得更加Natrual to writeapi

Apple在Xcode 8.0裏內置了可以幫你智能地完成大部分遷移工做的Migration Assistant. 但你仍是須要點幾個按鈕去完成這個過程.xcode

你馬上能遷移到Swift 2.3或者Swift 3.0, 若是你須要遷移回去2.2的話, 你隨時能夠到Xcode的菜單欄Edit -> Convert -> To Current Swift Syntax…遷移回去. 編譯器也能像Migration Assistant同樣智能, 若是你使用了舊的API的話, 編譯器會提供一個Fix-I他的選項來幫助你改爲正確的API.瀏覽器

這裏面最好的消息是, Swift 3.0的目標是做爲最後一個須要大量修改代碼的版本存在(API將會穩定下來). 往前看的話, 你將可以Swift的版本升級過程當中保持你的代碼不變. 固然, Swift的核心團隊沒辦法預知將來, 但他們承諾即便之後須要修改代碼以完成版本升級的時候, 也會保持長時間的向後兼容. 語言的穩定意味着愈來愈多保守的公司也採用Swift.

但二進制接口穩定下來的目標仍是沒能達成, 文章末尾你能看到這會帶來的影響

Swift Evolution計劃的提案

自從Swift宣佈開源以後, 社區成員已經提交了超過100個Proposal了. 大部分提案 (70個以上 )都在討論和修改以後被接受了. 那些被駁回的提案也都通過了激烈的討論. 最後, 由核心團隊來對全部提案作最後決定.

核心團隊跟社區的合做讓人印象深入. 實際上, Swift已經在Github上有了三萬個star了. 每週都會有幾個新的提案提交上去. 甚至連Apple本身的工程師在想要作出改變的時候也會打開Repo去寫提案.

在接下里的章節裏, 你會看到相似於[SE-0001]這樣子的超連接. 這些是Swift Evolution的提案編號. 這些提案都已經被接受而且將會在Swift 3.0的最終版本里實現. 每一個提案的連接點進去你都能看到關於每一個修改的所有細節.

API修改

Swift 3.0最大的升級是標準庫將會沿用以前的命名規範, API Design Guidelines包括了團隊在完成Swift 3.0的時候定下來的規範, 對於新程序員有更高的可讀性和易用性. 核心團隊堅守」Good API Design always consider the call site」. 他們力爭明確每個使用的場景. 如下是一些立刻會影響你的改變.

第一個參數的參數名

讓咱們先來顛覆一個你天天都在Swift裏的操做吧

函數和方法的第一個參數名不會再自動省略了, 除非你用了」_」. 以前你每次調用一個方法或者函數的時候都會自動省略第一個參數的參數名.

// 第一個是 Swift 2的寫法
// 第二個是 Swift 3的寫法
 
"RW".writeToFile("filename", atomically: true, encoding: NSUTF8StringEncoding)
"RW".write(toFile: "filename", atomically: true, encoding: NSUTF8StringEncoding)
 
SKAction.rotateByAngle(CGFloat(M_PI_2), duration: 10)
SKAction.rotate(byAngle: CGFloat(M_PI_2), duration: 10)
 
UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline)
UIFont.preferredFont(forTextStyle: UIFontTextStyleSubheadline)
 
override func numberOfSectionsInTableView(tableView: UITableView) -> Int
override func numberOfSections(in tableView: UITableView) -> Int
 
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView?
func viewForZooming(in scrollView: UIScrollView) -> UIView?
 
NSTimer.scheduledTimerWithTimeInterval(0.35, target: self, selector: #selector(reset), userInfo: nil, repeats: true)
NSTimer.scheduledTimer(timeInterval: 0.35, target: self, selector: #selector(reset), userInfo: nil, repeats: true)

咱們能夠注意到函數和方法的定義是怎麼使用相似於of, to, within這樣的介詞做爲外部參數名(而再也不放在函數或者方法名裏). 這一部分的修改是爲了提升可讀性.

若是調用方法的時候不須要介詞和外部參數名的時候, 你應該在第一個參數名前面加一個下劃線_來代表:

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { ... }
override func didMoveToView(_ view: SKView) { ... }

在不少編程語言裏, 不一樣的方法也許會用同一個名字但使用不一樣的參數名. Swift也不例外, 在API修改得更加直接以後, 如今咱們能夠經過重載來使用同一個方法名調用不一樣的方法. 下面是index()的兩種調用方式:

let names = ["Anna", "Barbara"]
if let annaIndex = names.index(of: "Anna") {
  print("Barbara's position: \(names.index(after: annaIndex))")
}

如今, 變量名的改變讓方法名可以更加具備一致性, 而且更容易理解

移除多餘的字詞

在以前Apple的庫裏面, 方法名一般會包含一個名字去代表返回值的類型, 但如今得益於Swift編譯器的類型推斷能力, 這件事情變得再也不那麼必要了. Swift核心團隊花了不少功夫去過濾雜音, 提取提案裏的真正需求, 就這樣不少重複的詞都去除掉了.

Objective-C和C語言庫的API變得更加接近原生Swift [SE-0005]:

// old way, Swift 2, followed by new way, Swift 3
let blue = UIColor.blueColor()
let blue = UIColor.blue()
 
let min = numbers.minElement()
let min = numbers.min()
 
attributedString.appendAttributedString(anotherString)
attributedString.append(anotherString)
 
names.insert("Jane", atIndex: 0)
names.insert("Jane", at: 0)
 
UIDevice.currentDevice()
UIDevice.current()

GCD和Core Graphics的現代化

提到頑固的老API, GCD和Core Graphics的語法都獲得了美化

GCD被用來處理諸如耗時運算和服務器通訊的多線程任務, 經過把任務移到別的線程, 你能夠防止本身的用戶界面線程被阻塞. libdispatch這個庫是用C語言寫的, 而且API也是C語言風格的. 這一套API如今被從新塑形成原生的Swift風格 [SE-0088]:

// Swift 2的寫法
let queue = dispatch_queue_create("com.test.myqueue", nil)
dispatch_async(queue) {
    print("Hello World")
}
 
// Swift 3的寫法
let queue = DispatchQueue(label: "com.test.myqueue")
queue.asynchronously {
  print("Hello World")
}

一樣的, Core Graphics也是C語言寫的, 而且之前都是使用很怪異的方法去調用. 新的用法就像這樣 [SE-0044]:

// Swift 2的寫法
let ctx = UIGraphicsGetCurrentContext()
let rectangle = CGRect(x: 0, y: 0, width: 512, height: 512)
CGContextSetFillColorWithColor(ctx, UIColor.blueColor().CGColor)
CGContextSetStrokeColorWithColor(ctx, UIColor.whiteColor().CGColor)
CGContextSetLineWidth(ctx, 10)
CGContextAddRect(ctx, rectangle)
CGContextDrawPath(ctx, .FillStroke)
UIGraphicsEndImageContext()
 
// Swift 3的寫法
if let ctx = UIGraphicsGetCurrentContext() {
    let rectangle = CGRect(x: 0, y: 0, width: 512, height: 512)
    ctx.setFillColor(UIColor.blue().cgColor)
    ctx.setStrokeColor(UIColor.white().cgColor)
    ctx.setLineWidth(10)
    ctx.addRect(rectangle)
    ctx.drawPath(using: .fillStroke)
 
    UIGraphicsEndImageContext()
}

枚舉值首字母大小寫

另外一個徹底不一樣的地方來自於你平常編寫Swift代碼的方式, 枚舉的值如今要用首字母小寫的駝峯命名法(lowerCamelCase). 這會讓他們看起來跟其餘Property和Values更加一致 (向Struct和Class靠攏) [SE-0006]:

// old way, Swift 2, followed by new way, Swift 3
UIInterfaceOrientationMask.Landscape
UIInterfaceOrientationMask.landscape
 
NSTextAlignment.Right
NSTextAlignment.right
 
SKBlendMode.Multiply
SKBlendMode.multiply

首字母大寫的駝峯命名(UpperCamelCase)如今只有類型(Types)和協議(Protocols). 你也許須要一些時間去適應, 這是Swift團隊對於一致性的執着.

方法名: 修改 / 返回修改後的副本

標準庫裏方法名的動詞和名詞都更加具備一致性, 你能夠根據action形成的效應去選擇命名方式(You choose a name based on side effects or the action taken). 最重要的原則是若是方法名包含了-ed或者-ing這樣的後綴的話, 那麼就能夠把方法當作一個名詞, 一個」名詞方法」將會返回一個值, 若是它不包含後綴, 那很像是一個動詞, 這些」動詞方法」將會在引用對象的內存裏進行操做, 也就是修改對象. 這裏有幾對動詞和名詞方法遵循這樣的原則 [SE-0006]:

customArray.enumerate()
customArray.enumerated()
 
customArray.reverse()
customArray.reversed()
 
customArray.sort() // 原來的寫法 .sortInPlace()
customArray.sorted()

這裏是一些他們的使用時的片斷:

var ages = [21, 10, 2] // 這裏是變量而不是常量, 因此能夠修改
ages.sort() // 在這裏進行修改, 當前值爲 [2, 10, 21]
 
for (index, age) in ages.enumerated() { // "-ed" 後綴意味着會返回一個修改後的副本
  print("\(index). \(age)") // 1. 2 \n 2. 10 \n 3. 21
}

函數類型

函數聲明和調用老是會要求用圓括號()把參數包起來:

func f(a: Int) { ... }
 
f(5)

不過, 當你使用函數做爲參數傳入的時候, 你也許會寫成這樣:

func g(a: Int -> Int) -> Int -> Int  { ... } // Swift 2的寫法

你也許會發現這樣可讀性不好, 參數在哪裏結束? 返回值在哪裏開始? Swift 3.0裏定義函數的正確方式是這樣的 [SE-0066]:

func g(a: (Int) -> Int) -> (Int) -> Int  { ... } // Swift 3的寫法

如今參數必須被圓括號()包住, 後面跟着返回值類型. 一切都變得很清晰, 具備連貫性, 函數的類型更容易看出來. 下面一個很明顯的對比:

// Swift 2的寫法
Int -> Float
String -> Int
T -> U
Int -> Float -> String
 
// Swift 3的寫法
(Int) -> Float
(String) -> Int
(T) -> U
(Int) -> (Float) -> String

API的補充

Swift 3.0最大的更新是現有API變得更加現代化, 有愈來愈多的Swift社區在致力於這件事, 包括一些額外的, 實用API

獲取類型

當你定義一個static的property或者method的的時候, 你必須經過他們的Type去調用他們:

CustomStruct.staticMethod()

若是你是在一個type裏寫代碼, 你仍是須要在type裏經過type名去調用static method. 爲了讓代碼更加清晰, 你能夠用Self去替代當前type的, S大寫的話Self表明type自己, s小寫的話self表明實例對象(instance)自己.

這裏是它的具體例子 [SE-0068]:

struct CustomStruct {
  static func staticMethod() { ... }

  func instanceMethod()
    Self.staticMethod() // 在結構體CustomStruct內部使用Self指向CustomStruct這個結構體自己
  }
}

let customStruct = CustomStruct()
customStruct.Self.staticMethod() // 在結構體實例(instance)裏的調用

Inline Sequences

sequence(first: next:)sequence(state: next:)是返回無限的sequence的全局方法. 你能夠給他們一個初始值或者一個可變的狀態, 而後他們會經過閉包進行懶加載 [SE-0094]

for view in sequence(first: someView, next: { $0.superview }) {
    // someView, someView.superview, someView.superview.superview, ...
}

你能夠經過prefix關鍵字來給sequence增長限制 [SE-0045]:

for x in sequence(first: 0.1, next: { $0 * 2 }).prefix(while: { $0 < 4 }) {
  // 0.1, 0.2, 0.4, 0.8, 1.6, 3.2
}

Miscellaneous Odds and Ends

  • #keypath()#selector()的工做方式很像, 可以幫助你糾正那些字符串類型的API

  • 你能夠在你想要使用的類型裏調用pi, 就像這樣: Float.pi, CGFloat.pi. 大部分時候編譯器還能夠幫你推導出類型: let circumference = 2 * .pi * radius[SE-0067]

  • 在那些老的Foundation裏NS開頭的類已經去掉了, 如今你能夠直接使用Calendar, Date而不是NSCalendarNSDate.

工具鏈的提高

Swift是一門語言, 編寫代碼的大部分工做須要開發環境, 對於Apple的開發者來講就是Xcode! 這些工具改變將會影響你天天編寫Swift代碼的方式.

Swift 3修復了編譯器和IDE的bug. 也提升了error和warning信息的可讀性. 就像你所預想的, 每一次更新, Swift在運行和編譯的時候都會變得更快:

  • 字典裏字符串的hashing速度是之前的三倍

  • 把對象從移到的速度是之前的24倍(某些狀況下)

  • 編譯器能夠一次性緩存多個文件(在整個模塊優化的時候)

  • 代碼大小優化能夠減小Swift代碼編譯後的大小. Apple的demo Demobots把可以把渲染後的大小變爲原來的77%

Xcode也學會了如何去解構原生Swift:

  • 當你右鍵點擊某個API的方法, 例如sort()的時候就會跳到它的定義, 之前你會被帶到一個晦澀難懂的頭文件裏. 但如今, Xcode 8, 如你預想的, 你會看到的sort()會是Array的一個拓展

  • Swift Snapshots會像Swift Evolution的Nightly Builds(天天編譯一次的最新版本). 它提供了一個機會去在徹底加入Xcode以前測試那些新的語法, Xcode 8能夠在playground里加載和運行Swift Snapshots

Swift包管理器

開源的Swift實際上包括了語言自己, 核心庫, 和包管理器這些repo. 這些套件的組合構成了咱們認識的那個Swift. Swift Package Manager定義了一個簡單的路徑結構, 可以把代碼讓你分享和導入項目裏

相似於你可能用過的Cocoapods或者Carthage同樣的包管理器, Swift的包管理器能夠下載和編譯依賴, 而且自動把他們連接到一塊兒去建立庫或者可執行程序. Swift 3是第一個包含包管理器的版本, 已經有超過1000個庫支持這個功能, 而且在以後的幾個月裏, 你會看到它的更多種形式

將來的計劃

就像以前所說的, Swift 3的目標是讓你可以保持你的代碼可以在之後版本里穩定下來, 避免將來會有破壞性的改變. 雖然如此, 但仍是有些相關的重要目標在這個版本里沒有完成, 泛化和ABI兼容.

泛化(Generics)還有遞歸協議約束, 以及新協議對於拓展(Extesion)的約束.(例如: 兩個具備相同元素的Array是沒法判斷是否相等的Euqaltble). 在這部分工做完成以前, Swift是不能完成ABI兼容的.

ABI兼容可讓不一樣版本編譯出來的程序和庫可以連接到一塊兒而且進行交互. 對於第三方庫來講這是很重要的, 它們能夠直接把本身的庫分享出去而沒必要連同代碼也一併提供, Swift的升級的時候, 這些第三方庫也沒必要升級代碼以及從新build框架.

另外, 二進制接口的穩定可讓程序再也不須要連同標準庫一塊兒打包, 就像如今發生在Xcode編譯出來的iOS和macOS程序同樣, 如今這些二進制可執行程序全都包括了2MB左右的Swift標準庫以保證他們可以在之後的操做系統裏也能運行.

總結起來, 你如今可讓你的代碼在版本升級的時候保持穩定, 沒必要作修改, 但編譯出來的二進制可執行程序則可能仍是會出現不兼容的情況.

以後的走向

在社區的努力下Swift還在一直進化中. 如今仍是Swift的萌芽期, 這門語言擁有無限的潛能和美好的將來. Swift已經能夠在Linux上運行, 再過幾年咱們也許極可能就能夠看到它在服務器上跑起來. 設計一門語言, 對於語言的修改確定會有它的好處和破壞接口穩定的可能性, 但當它穩定下來以後, 你會發現一切都是值得的, 這是一次可貴的機會可讓這門語言變得更好.

Swift的影響力也慢慢地在擴大, Apple is eating their own dogfood (Angular的梗, 諷刺Google不用本身家出的Angular JS), 蘋果的團隊已經在Music, Console, Sierra的畫中畫, Xcode的文檔瀏覽器和新的Swift Playground的iPad版本上用了Swift了.

對於非程序員人羣在iPad上學習Swift和通識教育有極大的推進做用.

簡單來講, Swift還在持續上升期: 命名變得更好, 代碼更清晰, 而且你也有工具可以幫助完成遷移, 若是你還想了解更多, 能夠看WWDC的視頻

相關文章
相關標籤/搜索