轉(zhuǎn)載自 Raywenderlich What's New in Swift?
Swift 3.0年末就會發(fā)布正式版, 并且會給所有Swift開發(fā)者帶來很多改變.
如果你沒有一直緊跟Swift Evolution的話, 你也許會想知道都有哪些改變, 以及它將會怎么影響你的代碼, 并且你該什么時(shí)候開始著手把代碼convert到3.0, 那這篇文章就是寫給你的.
這篇文章里, 我將會著筆于那些對你的代碼會有重大影響的Swift 3的新特性. 開始吧!
引言##
Swift 3.0預(yù)覽版在Xcode 8.0 beta已經(jīng)可以使用了. 當(dāng)Swift 3.0的改革已經(jīng)接近尾聲的時(shí)候, 接下來的幾個(gè)月也許還會再接受幾個(gè)提案, 功能性修改將會在2016年年末xcode發(fā)布正式版之后停止, 所以你必須等到那個(gè)時(shí)候才能把你的Swift 3應(yīng)用提交到App Store
為了讓開發(fā)者能夠開始著手移植到Swift 3.0, Apple已經(jīng)把Swift 2.3內(nèi)置到Xcode 8.0里. 對開發(fā)者來說, Swift 2.3跟2.2一樣, 但2.3支持WWDC上宣布的那些新的SDK和Xcode的新功能, Xcode 8.0出來之后, 你就可以提交使用Swift 2.3寫的app, 而不用等到3.0才能體驗(yàn)到那些新的特性.
我建議嘗試一下我們在playground實(shí)現(xiàn)的功能, 甚至可以在你的某個(gè)項(xiàng)目里跑一下Migration Assistant感受一下那些改變. 但Xcode 8.0和Swift 3.0還沒出正式版之前, 你還不可以提交app到App sSore里, 你也許需要考慮一下等Swift 3安定下來之后再把自己的所有代碼移植過去.
Swift 3的遷移工作##
轉(zhuǎn)到Swift 3.0的時(shí)候, 你必須意識到所有的文件都需要做修改! 這么大的改變是因?yàn)镃ocoa的API名字都改了. 或者為了更加明確, API會保持原樣, 但會有一個(gè)合適的名字給Objective-C和另一個(gè)給Swift. 接下里的幾年里, Swift 3.0將會讓Swift變得更加Natrual to write
Apple在Xcode 8.0里內(nèi)置了能夠幫你智能地完成大部分遷移工作的Migration Assistant. 但你還是需要點(diǎn)幾個(gè)按鈕去完成這個(gè)過程.
你立刻能遷移到swift 2.3或者swift 3. 如果你需要遷移回去2.2的話, 你隨時(shí)可以到Xcode的菜單欄Edit -> Convert -> To Current Swift Syntax…遷移回去. 編譯器也能像Migration Assistant一樣智能, 如果你使用了舊的API的話, 編譯器會提供一個(gè)Fix-I他的選項(xiàng)來幫助你改成正確的API.
這里面最好的消息是, Swift 3.0的目標(biāo)是作為最后一個(gè)需要大量修改代碼的版本存在(API將會穩(wěn)定下來). 往前看的話, 你將能夠Swift的版本升級過程中保持你的代碼不變. 當(dāng)然, Swift的核心團(tuán)隊(duì)沒辦法預(yù)知未來, 但他們承諾即使以后需要修改代碼以完成版本升級的時(shí)候, 也會保持長時(shí)間的向后兼容. 語言的穩(wěn)定意味著越來越多保守的公司也采用Swift.
但二進(jìn)制接口穩(wěn)定下來的目標(biāo)還是沒能達(dá)成, 文章末尾你能看到這會帶來的影響
Swift Evolution計(jì)劃的提案##
自從Swift宣布開源之后, 社區(qū)成員已經(jīng)提交了超過100個(gè)Proposal了. 大部分提案 (70個(gè)以上 )都在討論和修改之后被接受了. 那些被駁回的提案也都經(jīng)過了激烈的討論. 最后, 由核心團(tuán)隊(duì)來對所有提案做最后決定.
核心團(tuán)隊(duì)跟社區(qū)的合作讓人印象深刻. 實(shí)際上, Swift已經(jīng)在Github上有了三萬個(gè)star了. 每周都會有幾個(gè)新的提案提交上去. 甚至連Apple自己的工程師在想要做出改變的時(shí)候也會打開Repo去寫提案.
在接下里的章節(jié)里, 你會看到類似于[SE-0001]這樣子的超鏈接. 這些是Swift Evolution的提案編號. 這些提案都已經(jīng)被接受并且將會在Swift 3.0的最終版本里實(shí)現(xiàn). 每個(gè)提案的鏈接點(diǎn)進(jìn)去你都能看到關(guān)于每個(gè)修改的全部細(xì)節(jié).
API修改##
Swift 3.0最大的升級是標(biāo)準(zhǔn)庫將會沿用之前的命名規(guī)范, API Design Guidelines包括了團(tuán)隊(duì)在完成Swift 3.0的時(shí)候定下來的規(guī)范, 對于新程序員有更高的可讀性和易用性. 核心團(tuán)隊(duì)堅(jiān)守”Good API Design always consider the call site”. 他們力爭明確每一個(gè)使用的場景. 以下是一些馬上會影響你的改變.
第一個(gè)參數(shù)的參數(shù)名####
讓我們先來顛覆一個(gè)你每天都在swift里的操作吧
函數(shù)和方法的第一個(gè)參數(shù)名不會再自動省略了, 除非你用了”_”. 之前你每次調(diào)用一個(gè)方法或者函數(shù)的時(shí)候都會自動省略第一個(gè)參數(shù)的參數(shù)名.
// 第一個(gè)是 Swift 2的寫法
// 第二個(gè)是 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)
我們可以注意到函數(shù)和方法的定義是怎么使用類似于”of”, “to”, “with”和”in”這樣的介詞作為外部參數(shù)名( 而不再放在函數(shù)或者方法名里). 這一部分的修改是為了提高可讀性.
如果調(diào)用方法的時(shí)候不需要介詞和外部參數(shù)名的時(shí)候, 你應(yīng)該在第一個(gè)參數(shù)名前面加一個(gè)下劃線_來表明:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { ... }
override func didMoveToView(_ view: SKView) { ... }
在很多編程語言里, 不同的方法也許會用同一個(gè)名字但使用不同的參數(shù)名. Swift也不例外, 在API修改得更加直接之后, 現(xiàn)在我們可以通過重載來使用同一個(gè)方法名調(diào)用不同的方法. 下面是index()的兩種調(diào)用方式:
let names = ["Anna", "Barbara"]
if let annaIndex = names.index(of: "Anna") {
print("Barbara's position: \(names.index(after: annaIndex))")
}
現(xiàn)在, 變量名的改變讓方法名能夠更加具有一致性, 并且更容易理解
移除多余的字詞
在之前Apple的庫里面, 方法名通常會包含一個(gè)名字去表明返回值的類型, 但現(xiàn)在得益于Swift編譯器的類型推斷能力, 這件事情變得不再那么必要了. Swift核心團(tuán)隊(duì)花了很多功夫去過濾雜音, 提取提案里的真正需求, 就這樣很多重復(fù)的詞都去除掉了.
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的現(xiàn)代化
提到頑固的老API, GCD和Core Graphics的語法都得到了美化
GCD被用來處理諸如耗時(shí)運(yùn)算和服務(wù)器通信的多線程任務(wù), 通過把任務(wù)移到別的線程, 你可以防止自己的用戶界面線程被阻塞. libdispatch這個(gè)庫是用C語言寫的, 并且API也是C語言風(fēng)格的. 這一套API現(xiàn)在被重新塑造成原生的Swift風(fēng)格 [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語言寫的, 并且以前都是使用很怪異的方法去調(diào)用. 新的用法就像這樣 [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()
}
枚舉值首字母大小寫
另一個(gè)完全不同的地方來自于你日常編寫Swift代碼的方式, 枚舉的值現(xiàn)在要用首字母小寫的駝峰命名法(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)現(xiàn)在只有類型(Types)和協(xié)議(Protocols). 你也許需要一些時(shí)間去適應(yīng), 這是Swift團(tuán)隊(duì)對于一致性的執(zhí)著.
方法名: 修改 / 返回修改后的副本
標(biāo)準(zhǔn)庫里方法名的動詞和名詞都更加具有一致性, 你可以根據(jù)action造成的效應(yīng)去選擇命名方式(You choose a name based on side effects or the action taken). 最重要的原則是如果方法名包含了”-ed”或者”-ing”這樣的后綴的話, 那么就可以把方法當(dāng)做一個(gè)名詞, 一個(gè)”名詞方法”將會返回一個(gè)值, 如果它不包含后綴, 那很像是一個(gè)動詞, 這些”動詞方法”將會在引用對象的內(nèi)存里進(jìn)行操作, 也就是修改對象. 這里有幾對動詞和名詞方法遵循這樣的原則 [SE-0006]:
customArray.enumerate()
customArray.enumerated()
customArray.reverse()
customArray.reversed()
customArray.sort() // 原來的寫法 .sortInPlace()
customArray.sorted()
這里是一些他們的使用時(shí)的片段:
var ages = [21, 10, 2] // 這里是變量而不是常量, 所以可以修改
ages.sort() // 在這里進(jìn)行修改, 當(dāng)前值為 [2, 10, 21]
for (index, age) in ages.enumerated() { // "-ed" 后綴意味著會返回一個(gè)修改后的副本
print("\(index). \(age)") // 1. 2 \n 2. 10 \n 3. 21
}
函數(shù)類型
函數(shù)聲明和調(diào)用總是會要求用圓括號()把參數(shù)包起來:
func f(a: Int) { ... }
f(5)
不過, 當(dāng)你使用函數(shù)作為參數(shù)傳入的時(shí)候, 你也許會寫成這樣:
func g(a: Int -> Int) -> Int -> Int { ... } // Swift 2的寫法
你也許會發(fā)現(xiàn)這樣可讀性很差, 參數(shù)在哪里結(jié)束? 返回值在哪里開始? Swift 3.0里定義函數(shù)的正確方式是這樣的 [SE-0066]:
func g(a: (Int) -> Int) -> (Int) -> Int { ... } // Swift 3的寫法
現(xiàn)在參數(shù)必須被圓括號()包住, 后面跟著返回值類型. 一切都變得很清晰, 具有連貫性, 函數(shù)的類型更容易看出來. 下面一個(gè)很明顯的對比:
// Swift 2的寫法
Int -> Float
String -> Int
T -> U
Int -> Float -> String
// Swift 3的寫法
(Int) -> Float
(String) -> Int
(T) -> U
(Int) -> (Float) -> String
API的補(bǔ)充
Swift 3.0最大的更新是現(xiàn)有API變得更加現(xiàn)代化, 有越來越多的Swift社區(qū)在致力于這件事, 包括一些額外的, 實(shí)用API
獲取類型
當(dāng)你定義一個(gè)static的property或者method的的時(shí)候, 你必須通過他們的Type去調(diào)用他們:
CustomStruct.staticMethod()
如果你是在一個(gè)type里寫代碼, 你還是需要在type里通過type名去調(diào)用static method. 為了讓代碼更加清晰, 你可以用Self去替代當(dāng)前type的, S大寫的話Self代表type本身, s小寫的話self代表實(shí)例對象(instance)本身.
這里是它的具體例子 [SE-0068]:
struct CustomStruct {
static func staticMethod() { ... }
func instanceMethod()
Self.staticMethod() // 在結(jié)構(gòu)體CustomStruct內(nèi)部使用Self指向CustomStruct這個(gè)結(jié)構(gòu)體本身
}
}
let customStruct = CustomStruct()
customStruct.Self.staticMethod() // 在結(jié)構(gòu)體實(shí)例(instance)里的調(diào)用
Inline Sequences
sequence(first: next:)和sequence(state: next:)是返回?zé)o限的sequence的全局方法. 你可以給他們一個(gè)初始值或者一個(gè)可變的狀態(tài), 然后他們會通過閉包進(jìn)行懶加載 [SE-0094]
for view in sequence(first: someView, next: { $0.superview }) {
// someView, someView.superview, someView.superview.superview, ...
}
你可以通過prefix關(guān)鍵字來給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
- 你可以在你想要使用的類型里調(diào)用pi, 就像這樣: Float.pi, CGFloat.pi. 大部分時(shí)候編譯器還可以幫你推導(dǎo)出類型: let circumference = 2 * .pi * radius[SE-0067]
- 在那些老的Fundation里NS開頭的類已經(jīng)去掉了, 現(xiàn)在你可以直接使用Calendar, Date而不是NSCalendar和NSDate.
工具鏈的提升
Swift是一門語言, 編寫代碼的大部分工作需要開發(fā)環(huán)境, 對于Apple的開發(fā)者來說就是Xcode! 這些工具改變將會影響你每天編寫Swift代碼的方式.
Swift 3修復(fù)了編譯器和IDE的bug. 也提高了error和warning信息的可讀性. 就像你所預(yù)想的, 每一次更新, Swift在運(yùn)行和編譯的時(shí)候都會變得更快:
- 字典里字符串的hashing速度是以前的三倍
- 把對象從棧移到堆的速度是以前的24倍(某些情況下)
- 編譯器可以一次性緩存多個(gè)文件(在整個(gè)模塊優(yōu)化的時(shí)候)
- 代碼大小優(yōu)化可以減少Swift代碼編譯后的大小. Apple的demo Demobots把能夠把渲染后的大小變?yōu)樵瓉淼?7%
Xcode也學(xué)會了如何去解構(gòu)原生Swift:
- 當(dāng)你右鍵點(diǎn)擊某個(gè)API的方法, 例如sort()的時(shí)候就會跳到它的定義, 以前你會被帶到一個(gè)晦澀難懂的頭文件里. 但現(xiàn)在, Xcode 8, 如你預(yù)想的, 你會看到的sort()會是Array的一個(gè)拓展
- Swift Snapshots會像Swift Evolution的Nightly Builds(每天編譯一次的最新版本). 它提供了一個(gè)機(jī)會去在完全加入Xcode之前測試那些新的語法, Xcode 8可以在playground里加載和運(yùn)行Swift Snapshots
Swift包管理器
開源的Swift實(shí)際上包括了語言本身, 核心庫, 和包管理器這些repo. 這些套件的組合構(gòu)成了我們認(rèn)識的那個(gè)Swift. Swift Package Manager定義了一個(gè)簡單的路徑結(jié)構(gòu), 能夠把代碼讓你分享和導(dǎo)入項(xiàng)目里
類似于你可能用過的Cocoapods或者Carthage一樣的包管理器, Swift的包管理器可以下載和編譯依賴, 并且自動把他們鏈接到一起去創(chuàng)建庫或者可執(zhí)行程序. Swift 3是第一個(gè)包含包管理器的版本, 已經(jīng)有超過1000個(gè)庫支持這個(gè)功能, 并且在之后的幾個(gè)月里, 你會看到它的更多種形式
未來的計(jì)劃
就像之前所說的, Swift 3的目標(biāo)是讓你能夠保持你的代碼能夠在以后版本里穩(wěn)定下來, 避免未來會有破壞性的改變. 雖然如此, 但還是有些相關(guān)的重要目標(biāo)在這個(gè)版本里沒有完成, 泛化和ABI兼容.
泛化(Generics)還有遞歸協(xié)議約束, 以及新協(xié)議對于拓展(Extesion)的約束.(例如: 兩個(gè)具有相同元素的Array是無法判斷是否相等的Euqaltble). 在這部分工作完成之前, Swift是不能完成ABI兼容的.
ABI兼容可以讓不同版本編譯出來的程序和庫能夠鏈接到一起并且進(jìn)行交互. 對于第三方庫來說這是很重要的, 它們可以直接把自己的庫分享出去而不必連同代碼也一并提供, Swift的升級的時(shí)候, 這些第三方庫也不必升級代碼以及重新build框架.
另外, 二進(jìn)制接口的穩(wěn)定可以讓程序不再需要連同標(biāo)準(zhǔn)庫一起打包, 就像現(xiàn)在發(fā)生在Xcode編譯出來的iOS和macOS程序一樣, 現(xiàn)在這些二進(jìn)制可執(zhí)行程序全都包括了2MB左右的Swift標(biāo)準(zhǔn)庫以保證他們能夠在以后的操作系統(tǒng)里也能運(yùn)行.
總結(jié)起來, 你現(xiàn)在可以讓你的代碼在版本升級的時(shí)候保持穩(wěn)定, 不必做修改, 但編譯出來的二進(jìn)制可執(zhí)行程序則可能還是會出現(xiàn)不兼容的狀況.
之后的走向
在社區(qū)的努力下Swift還在一直進(jìn)化中. 現(xiàn)在還是Swift的萌芽期, 這門語言擁有無限的潛能和美好的未來. Swift已經(jīng)可以在Linux上運(yùn)行, 再過幾年我們也許很可能就可以看到它在服務(wù)器上跑起來. 設(shè)計(jì)一門語言, 對于語言的修改肯定會有它的好處和破壞接口穩(wěn)定的可能性, 但當(dāng)它穩(wěn)定下來之后, 你會發(fā)現(xiàn)一切都是值得的, 這是一次難得的機(jī)會可以讓這門語言變得更好.
Swift的影響力也慢慢地在擴(kuò)大, Apple is eating their own dogfood (Angular的梗), 蘋果的團(tuán)隊(duì)已經(jīng)在Music, Console, Sierra的畫中畫, Xcode的文檔瀏覽器和新的Swift Playground的iPad版本上用了Swift了.
對于非程序員人群在iPad上學(xué)習(xí)Swift和通識教育有極大的推動作用.
簡單來說, Swift還在持續(xù)上升期: 命名變得更好, 代碼更清晰, 并且你也有工具能夠幫助完成遷移, 如果你還想了解更多, 可以看WWDC的視頻