Swift 3即將發(fā)布,有何亮點?
譯文
簡介
Swift 3即將在今年晚些時候正式發(fā)行,它將為所有Swift開發(fā)人員帶來重大影響。如果你沒有一直密切關(guān)注Swift項目的進(jìn)展,你可能想知道Swift 3中將有哪些變化、它將如何影響你的編碼以及何時開始將你的代碼移植到Swift 3。
在本文中,我將向你重點介紹Swift 3將為你的編碼帶來的重大變化。至于是哪些變化,讓我們來一探究竟吧!
準(zhǔn)備工作
Swift 3的預(yù)覽版本已經(jīng)隨同Xcode 8 beta版本一起發(fā)行了。雖然Swift 3的巨大改進(jìn)已經(jīng)不大可能,但在未來幾個月中仍可能還有幾個可被接受的變化。當(dāng)Xcode在2016年底放出其GM版本時其功能將不再發(fā)生更改,所以在你把Swift 3應(yīng)用程序發(fā)布到蘋果應(yīng)用程序商店之前你不得不繼續(xù)推遲發(fā)行你的應(yīng)用。
為了允許開發(fā)人員根據(jù)他們自己的需求對Swift 3進(jìn)行調(diào)整,蘋果特意在Xcode 8中提供了Swift 2.3版本作為Swift的小規(guī)模更新。作為一名開發(fā)人員,Swift 2.3很大程度上與Swift 2.2一致,但支持很多蘋果在WWDC大會上宣布的新的SDK和Xcode特征。一旦Xcode 8越過測試版本,你就能夠提交使用Swift 2.3開發(fā)的應(yīng)用程序——如果你還沒有把你的代碼遷移到Swift 3的話。
因此,我建議你試驗性地嘗試一下我們在本文中討論的特點,甚至在你的項目上運行一下遷移助手(Migration Assistant);這樣,你可以感受一下正在發(fā)生的變化。但是,因為你在Xcode 8和Swift 3正式發(fā)行前還不能把應(yīng)用程序發(fā)布到蘋果應(yīng)用程序商店,所以,你可能要考慮等一切有了著落后再將代碼移植到Swift 3。
遷移到Swift 3
當(dāng)轉(zhuǎn)換為Swift 3版本時,你會發(fā)現(xiàn)幾乎每個文件都需要更改!這主要是因為所有Cocoa API名稱都已更改?;蛘撸_切地講,盡管API相同,但還是有一個名稱適合Objective C而另外一個名稱適合Swift。Swift 3的目標(biāo)是要實現(xiàn)在未來多年中編寫Swift代碼將變得更為自然。
蘋果公司在Xcode 8中包括了一個遷移助手程序,它能夠出色地幫助解決上述的大規(guī)模修改代碼的問題。不過,你不要為還得修改幾個地方而感到驚訝,因為移植器程序在這些地方是不會自動處理的。
你可以立即把以前的代碼轉(zhuǎn)換為Swift 2.3或Swift 3。如果你需要把它改回來,你可以隨時使用Xcode中提供的命令“Edit > Convert > To Current Swift Syntax…”。所幸的是,編譯器具有與遷移助手一樣的智能性。如果你在方法調(diào)用上偶然使用了舊式API,編譯器會提供一個“Fix It”選項,它將幫助你使用正確的新式API。
最好的消息是,Swift 3的目的是使其成為大規(guī)模更改源碼的最后一個版本。所以,從長遠(yuǎn)來看,你應(yīng)該保持你的程序的Swift代碼從一個版本升級到另一個版本。雖然Swift核心團(tuán)隊不能預(yù)測未來,但他們已經(jīng)承諾,如果他們確實需要打破源代碼兼容性,他們將提供長期的廢棄代碼重用支持。這意味著,該語言已經(jīng)取得源碼的穩(wěn)定性保證,這將有助于鼓勵保守的公司采用它。
這就是說,實現(xiàn)二進(jìn)制穩(wěn)定的目標(biāo)還沒有實現(xiàn)。在本文末尾,你會發(fā)現(xiàn)更多的方面受這種影響。
實現(xiàn)Swift進(jìn)化建議
目前,社區(qū)成員已經(jīng)就swift改進(jìn)方面提交了超過100條的建議,因為它是開放源碼的。大量的建議 (目前70多條)在經(jīng)過討論和修改后已被接受。當(dāng)然,那些已被駁回的建議也引發(fā)了一些激烈的討論。然而,最終,將由核心團(tuán)隊就各種建議作最后決定。
核心團(tuán)隊和更廣泛的社區(qū)之間的合作已經(jīng)令人印象深刻。事實上,Swift已經(jīng)在Github上贏得了3萬顆星。每周都會有幾項新建議被提交。即使是蘋果工程師當(dāng)他們想要進(jìn)行更改時也要通過Github代碼倉庫提交建議。
在下面的段落中,你將看到如[SE-0001]這樣的鏈接標(biāo)簽。它們是Swift演化建議數(shù)字。這里提供的建議編號都已被接受,并將在最終的Swift 3.0正式發(fā)行版本中使用。我之所以提供這些數(shù)據(jù)是為了方便你找到每個特定更改的全部詳情對應(yīng)的鏈接。
API變化
Swift 3中最大的更新就是其標(biāo)準(zhǔn)庫API都使用了一致的命名約定。蘋果的API設(shè)計指導(dǎo)原則(https://swift.org/documentation/api-design-guidelines/)中指明了開發(fā)團(tuán)隊在構(gòu)建Swift 3項目時應(yīng)當(dāng)遵循的規(guī)則,這些規(guī)定極有助于新程序員對源碼的可讀性和可訪問性。核心團(tuán)隊工作的一條重要指導(dǎo)原則是“好的API設(shè)計總是為調(diào)用方考慮”。他們力圖使代碼變得清晰易用。費話少說,下面將展示最有可能影響你的API中的一些變化。
首參數(shù)標(biāo)簽
讓我們從一個你可能每天都使用的Swift例子開始吧。
現(xiàn)在,函數(shù)和方法的第一個參數(shù)總是帶有一個標(biāo)簽,除非你以其它方式請求去掉。以前,當(dāng)你調(diào)用一個函數(shù)或方法時總是省略第一個參數(shù)標(biāo)簽(SE-0046)。參考如下代碼:
//注意下面代碼中第一行是老式的Swift 2形式,第二行對應(yīng)新式的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”、“with”和“in”這樣的介詞。這些都是努力優(yōu)化代碼可讀性的一部分。
如果方法調(diào)用可讀性在不使用介詞時就已經(jīng)很好并且不需要標(biāo)簽,那么你應(yīng)明確使用下劃線來排除第一個參數(shù)名稱:
- override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { ... }
- override func didMoveToView(_ view: SKView) { ... }
在許多編程語言中,方法可能共享使用基本命名并提供不同的參數(shù)名稱。Swift也沒有例外。因此,現(xiàn)在你會遇到大量的重載方法命名,因為以前的Api都已被翻譯得更直接易讀。下面提供了index()方法的兩種表達(dá)形式:
- let names = ["Anna", "Barbara"]
- if let annaIndex = names.index(of: "Anna") {
- print("Barbara's position: \(names.index(after: annaIndex))")
- }
總之,參數(shù)名稱的改變使得方法命名更一致和更容易學(xué)習(xí)。
忽略不必要的關(guān)鍵字
在以前若干個版本的蘋果開發(fā)庫中,方法都是包括一個名稱以表明它們的返回值。由于Swift編譯器提供了新的類型檢查功能,這樣的格式已經(jīng)沒大有必要了。開發(fā)團(tuán)隊認(rèn)真審視了如何過濾掉所有不必要的部分,從而只有必需的部分保留下來;因此,大量的單詞已被刪除。
在如何把Objective C轉(zhuǎn)化為本機(jī)Swift方面,新的API已經(jīng)變得十分明智(SE-005):
//注意下面代碼中第一行是老式的Swift 2形式,第二行對應(yīng)新式的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及核心圖形庫更新
提到頑固的老式API,GCD及核心圖形庫都迫切需要改造。如今,這項工作已經(jīng)開始。
GCD庫適用于長時間的計算或者與服務(wù)器通信這樣的多線程任務(wù)。通過將活動移動到一個不同的線程,你可以防止用戶界面鎖定。Libdispatch庫是使用C語言編寫的并一直保持C風(fēng)格的API?,F(xiàn)在,在本機(jī)Swift中,該API已經(jīng)被重寫(SE-0088)。請參考如下代碼:
- // old way, Swift 2
- let queue = dispatch_queue_create("com.test.myqueue", nil)
- dispatch_async(queue) {
- print("Hello World")
- }
- // new way, Swift 3
- let queue = DispatchQueue(label: "com.test.myqueue")
- queue.async {
- print("Hello World")
- }
類似地,核心圖形庫部分也是使用C語言編寫的并以老風(fēng)格調(diào)用(SE-0044)。如今也得以改寫。請參考下面的示例代碼展示:
- // old way, 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()
- // new way, 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 3中的枚舉表達(dá)中使用小駝峰(lowerCamelCase,即第一個詞首字符小寫,其他的后續(xù)每一個詞的首字母大寫)表達(dá)方式,這可使之與其它屬性(或者值)保持一致(SE-0006):
// 注意:下面代碼中第一行是Swift 2方式,緊接著的是新式的Swift 3寫法
- UIInterfaceOrientationMask.Landscape
- UIInterfaceOrientationMask.landscape
- NSTextAlignment.Right
- NSTextAlignment.right
- SKBlendMode.Multiply
- SKBlendMode.multiply
如今,大駝峰寫法僅為類型和協(xié)議命名而保留。盡管習(xí)慣這一點可能還要花費一點時間,但Swift開發(fā)團(tuán)隊的確是在努力確保整體表達(dá)的盡可能一致性。
方法命名上的變化
標(biāo)準(zhǔn)庫在方法命名方面也變得更加一致。一般的建議是,你應(yīng)當(dāng)根據(jù)要創(chuàng)建的方法的作用或者行為進(jìn)行命名。經(jīng)驗法則是,如果方法包含一個像"-ed"或"-ing"這樣的后綴,那么認(rèn)為該方法是名詞性的。名詞性的方法會返回一個值。如果方法沒有后綴,那么它很可能是命令式的動詞性方法。這些"動詞"方法在引用內(nèi)存上執(zhí)行操作;這也被稱為原地修改(modifying in place)。標(biāo)準(zhǔn)庫中存在好幾對遵循此名詞/動詞約定的方法(SE-0006)。下面是其中的幾個:
- customArray.enumerate()
- customArray.enumerated()
- customArray.reverse()
- customArray.reversed()
- customArray.sort() // changed from .sortInPlace()
- customArray.sorted()
下面的代碼片斷給出了它們的實際用法:
- var ages = [21, 10, 2] // variable, not constant, so you can modify it
- ages.sort() // modified in place, value now [2, 10, 21]
- for (index, age) in ages.enumerated() { // "-ed" noun returns a copy
- print("\(index). \(age)") // 1. 2 \n 2. 10 \n 3. 21
- }
函數(shù)類型
函數(shù)聲明與函數(shù)調(diào)用從來都是要求使用小括號把它們的參數(shù)包圍起來:
- func f(a:Int){…}
- f(5)
然而,當(dāng)你使用函數(shù)類型作為參數(shù)時,你在Swift 2中可以這樣寫:
- func g(a: Int -> Int) -> Int -> Int { ... } // old way, Swift 2
你可能注意到了這種表達(dá)方式有點難懂。參數(shù)部分將在哪里結(jié)束?返回類型是從哪里開始的?在Swift 3中定義上述函數(shù)的語法是這樣表達(dá)的(SE-0066):
- func g(a: (Int) -> Int) -> (Int) -> Int { ... } // new way, Swift 3
現(xiàn)在,你注意到了,參數(shù)列表使用小括號包圍,后面跟著的是返回類型。這種表達(dá)相當(dāng)清楚;當(dāng)然,函數(shù)類型也更易于識別。下面的小例子提供了更有力的對比:
- // old way, Swift 2
- Int -> Float
- String -> Int
- T -> U
- Int -> Float -> String
- // new way, Swift 3
- (Int) -> Float
- (String) -> Int
- (T) -> U
- (Int) -> (Float) -> String
API擴(kuò)展
盡管Swift 3的最大更新點主要集中在現(xiàn)有API的現(xiàn)代化方面,但是仍然存在很多的Swift社團(tuán)在這方面進(jìn)行努力工作,并且為Swift API提供許多非常有益的擴(kuò)展。
訪問容器類型
當(dāng)你定義一個靜態(tài)屬性或者方法時,你常常在其上進(jìn)行這樣的調(diào)用:
- CustomStruct.staticMethod()
如果你在某個類型的上下文中寫代碼,你仍然需要包括此類型的名字來調(diào)用其相應(yīng)的靜態(tài)方法。為了使這種操作更清晰明了,現(xiàn)在你可以調(diào)用Self來取得容器類型。這里大寫的Self代表self的類型,而小寫的self則代表self的實例。
下面代碼展示了其工作方式(SE-0068):
- struct CustomStruct {
- static func staticMethod() { ... }
- func instanceMethod() {
- Self.staticMethod() // in the body of the type
- }
- }
- let customStruct = CustomStruct()
- customStruct.Self.staticMethod() // on an instance of the type
內(nèi)聯(lián)序列
- sequence(first:next:)和sequence(state:next:)
都是能夠返回?zé)o限序列的全局函數(shù)。如果你給它們指定一個初始值和一個可變狀態(tài),它們將自動應(yīng)用閉包(SE-0094)。例如:
- for view in sequence(first: someView, next: { $0.superview }) {
- // someView, someView.superview, someView.superview.superview, ...
- }
當(dāng)然,你還可以使用prefix來約束序列(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
- }
其他雜項
l #keyPath()工作方式類似于#sselector(),這將有助于幫助程序員迅速修改強(qiáng)類型Api中的拼寫錯誤。
l 現(xiàn)在你可以像這樣使用pi:Float.pi,CGFloat.pi。在大多數(shù)情況下,編譯器能夠推斷出相應(yīng)的類型,例如:
- let circumference=2*.pi*radius(SE-0067)
l NS前綴被從一些老式基本類型上移除,現(xiàn)在你可以直接使用Calendar和Date,而不必再使用像NSCalendar和NSDate這樣形式。
開發(fā)工具改進(jìn)
Swift是一種編程語言,因此,編寫Swift程序的很大一部分自然要涉及到開發(fā)環(huán)境的使用——這對于蘋果開發(fā)者來說很可能是Xcode!開發(fā)環(huán)境工具的變化也必將影響你編寫Swift代碼的每一天。
Swift3進(jìn)一步修復(fù)了編譯器和IDE功能中的錯誤。此外,它還提高了報告錯誤和警告消息的精確度。如你所料,在每一個版本中,Swift運行速度都變得越來越快,這與其運行和編譯方式是密不可分的:
l 通過改進(jìn)字符串哈希技術(shù),字符串字典方面達(dá)到3倍的加速
l 通過把對象從堆移動到堆棧存儲,在某些情況下達(dá)到24倍的加速效率
l 編譯器現(xiàn)在能夠一次緩存多個文件(在作整個模塊優(yōu)化時)
l 代碼大小優(yōu)化技術(shù)極大地減少了Swift代碼的編譯后尺寸。例如,蘋果公司提供的著名的演示程序Demobots(https://developer.apple.com/library/ios/samplecode/DemoBots/Introduction/Intro.html)經(jīng)代碼優(yōu)化技術(shù)處理后其編譯后尺寸減少到原來的77%
另外,值得注意的是,如今的Xcode也正在學(xué)著如何以本機(jī)Swift方式進(jìn)行“思考”:
l 以前,當(dāng)你右鍵單擊像sort()這樣的API對象時系統(tǒng)將會跳轉(zhuǎn)到其所在定義,于是你會被導(dǎo)航到一個神秘的頭文件。在現(xiàn)在的Xcode 8版本中,你會看到sort()成為數(shù)組的擴(kuò)展對象,而這正是你所期望的。
l Swift的快照技術(shù)很像其演變過程中的Nightly發(fā)行版本一樣。這為其充分融合到Xcode前提供了一種使用這種新語法的機(jī)會。Xcode 8支持在Playground中加載和運行Swift快照。
Swift包管理器
開放源碼的Swift語言實際上是一個包的集合,包括語言、核心庫和包管理器共三個部分。其中,Swift程序包管理器(Package Manager)為你想分享并導(dǎo)入到其他項目的任何Swift代碼定義了一個簡單的目錄結(jié)構(gòu)。
類似于軟件包管理器,你可能習(xí)慣了Cocoapods或Carthage這樣的工具,Swift的軟件包管理器將下載依賴項,編譯它們,并把它們鏈接起來以創(chuàng)建庫和可執(zhí)行文件。Swift3是包括Swift程序包管理器的第一個版本?,F(xiàn)在有1,000多個庫為之提供支持;在未來幾個月里,你將會看到為之增加的更多的支持。
計劃中的未來特征
前面提到,Swift3的目的是保持你的代碼在新版本的不斷推出中能夠保持向前有良好的兼容性。雖然情況是這樣,但是對于時下這個版本來說還有一些較難達(dá)成的內(nèi)容,即泛型技術(shù)的引入和應(yīng)用程序二進(jìn)制接口(ABI)的穩(wěn)定等目標(biāo)。
泛型引入將包括遞歸協(xié)議約束以及支持約束擴(kuò)展符合新協(xié)議(例如,一個Equatable元素數(shù)組應(yīng)當(dāng)是Equatable,等等)。在完成這些功能之前,Swift尚無法確保ABI穩(wěn)定性。
ABI穩(wěn)定將允許使用不同版本的Swift編譯的應(yīng)用程序和庫能夠鏈接到一起并彼此交互。這是在第三方庫沒有提供源代碼時仍能夠進(jìn)行框架遷移必需的關(guān)鍵一步,因為新版本的 Swift不僅需要這些第三方庫來更新他們的代碼,而且能夠重建其框架。
此外,ABI穩(wěn)定性將使得Swift標(biāo)準(zhǔn)庫不必與二進(jìn)制文件一同發(fā)行,對于目前使用Xcode創(chuàng)建的iOS和macOS應(yīng)用程序情況就是這樣。現(xiàn)在的二進(jìn)制文件被捆綁了2 MB大小的額外文件尺寸,以確保它們運行于未來的操作系統(tǒng)上。
現(xiàn)在來總結(jié)一下,你現(xiàn)在可以使你的源代碼從版本更換到另一個版本,但編譯后的二進(jìn)制兼容性在從一個版本到另一個版本方面還沒有實現(xiàn)。
小結(jié)
Swift將隨著社團(tuán)公眾的最佳做法繼續(xù)發(fā)展。雖然仍處于起步階段,該語言已經(jīng)取得了很大成功并預(yù)示著一個輝煌的未來。Swift已經(jīng)成功地運行在Linux 上,而且你可能會看到它在未來的幾年中將運行于除設(shè)備外的服務(wù)器上。從零開始設(shè)計一種語言當(dāng)然會有其優(yōu)勢來打破ABI穩(wěn)定性。這是一個沒有帶著遺憾糾正語言的獨特機(jī)會。
Swift也在不斷擴(kuò)大其覆蓋面。蘋果公司正在把Swift應(yīng)用于自己產(chǎn)品的開發(fā)方面。例如,蘋果團(tuán)隊在ipad音樂應(yīng)用程序開發(fā),控制臺開發(fā),Sierra中的畫中畫開發(fā),Xcode文檔查看器和新的Swift Playground應(yīng)用等方面都在廣泛使用Swift語言。
說到這里,當(dāng)前又有一股很大的熱潮使非程序員爭相學(xué)習(xí)Swift,無論在iPad開發(fā)方面還是通過教育目的方面。
如今的Swift正處在持續(xù)上升期:名字更好、代碼更清晰易讀,而且你還有輔助工具幫助你實現(xiàn)代碼遷移。如果你想要更深入地挖掘Swift,你可以看看WWDC會議錄像(https://developer.apple.com/videos/wwdc2016/)。
在2016年底Swift 3正式發(fā)行之時肯定會有更多的功能加入。我們將基于這里的所有更新,并繼續(xù)關(guān)注新的教程、書籍公告和相關(guān)視頻推出。