Swift 3 新特性
原文:What's New in Swift 3??,作者:Ben Morrow,譯者:kmyhy
Swift 3將于今年下半年推出,為Swift開發者們帶來了很多核心代碼的改變。如果你沒有關注過 Swift Evolution 項目,你可能會好奇Swift 3中有什么改變,它會對你的代碼帶來什么影響,以及何時可以將代碼移植到Swift 3。本文就將為你答疑解惑!
本文中,我將重點介紹 Swift 3 中的重大改變,以及這些改變對你的代碼的深刻影響。讓我們開始吧!
開始
目前,Swift 3 預覽版僅在 Xcode 8 beta版中可用。在未來幾個月中,Swift 3 仍然在不斷改變中,它還會發生一些變化。在 2016 年末,Xcode GM 發布時,Swift 3 的新特性才會穩定。所以各位開發者要沉住氣,直到那個時候才可以向 App Store 提交用 Swift 3 編寫的 App。
為了便于開發者們將代碼遷移到 Swift 3,蘋果在 Xcode 8 中增加了一個 Swift 版本,即 Swift 2.3,對 Swift 作了一個小小的升級。如果你是一個開發者,Swift 2.3 和 2.2 實際并無不同,但 Swift 2.3版本能夠支持本次WWDC中提到的各種新的SDK 和 Xcode 特性。在 Xcode 8 推出 beta 版時,你可以用 Swift 2.3 來提交你的 app,而不用將代碼遷移到 Swift 3。
我建議你在 Playground 中測試本文討論的新特性,還可以用你的某個項目來測試 Migration Assistant,以便你對這些改變有所了解。由于在 Xcode 8 beta 下一個版本及 Siwft 3 正式發布之前,你無法向 App Store 提交 App,那么我建議暫時先不要講代碼遷移到Swift 3。
升級到 Swift 3
升級到Swift 3時,你會發現基本上每個文件都需要改動!之所以這樣,是因為所有的 Cocoa API 名稱都改變了。簡而言之,API還是原來的 API,但這個 API 在 Objective-C 中是一種叫法,而在 Swift 中是另一種叫法。Swift 3的語法書寫起來要更貼近于自然語言。
在Xcode 8中,蘋果提供了Migration Assistant,它可以完成大部分的遷移工作。當然,仍然有一部分工作是需要你手動完成的。
你可以立即將代碼升級到 2.3 或者 3.0。如果你需要將代碼又轉回來,你可以用 Xcode 的 Edit > Convert > To Current Swift Syntax… 菜單。編譯器會和 Migrateion Assistant 一樣智能。如果你在調用方法時,偶然使用了老的 API,編譯器會顯示一個 Fixt-It 選項,讓你使用正確的新 API。幸好 Swift 3 在最終發布時,才會停止改變源代碼。因此,你可以將你的 Swift 代碼保存為不同的版本。但是 Swift 核心團隊不能保證這一點以后不會改變,如果在某個時候不在保證源碼上的兼容,他們會提供一個較長的過渡期。這意味著源碼是穩定的,這樣能鼓勵更多的保守的公司去使用它。
這也說明,二進制穩定的目標還沒有達到。本文最后將討論這將導致的影響。
已實現的 Swift Evolution 提議
自從 Swift 開源以來,社區成員已經提交了超過 100 個改進建議。其中大部分(70多個)提議經過討論修改之后已經被接受。還有一些仍然在激烈的爭論之后被拒絕了。但最終,所有提議都要經過核心團隊的最終拍板。
核心團隊和廣大社區之間的合作是卓有成效的。事實上,Swift 在 Github 上獲得了 30,000 多顆星。每周都會有新的提議提交,周周如此。甚至蘋果的工程師也會在 Github 上提交他們的提議。
在下一節中,你會看到一些類似 [SE-0001] 這樣的標注。這是已經接受的提議編號,并且將在最終版的 Swift 3.0 中進行實現。每項提議都會標出,以便你查看每項改變的具體細節。
API 的改變
Swift 3 中最大的改變是標準庫中在每個庫中都采用了統一命名方式。API Design Guidleines中包含了這些規則,核心團隊在構建 Swift 3 時采用了這些規則,對新手來說,這高度增強了可讀性和易用性。核心團隊遵循的是"好的 API 設計應當總是從調用者的角度看待問題"的原則。他們努力讓 API 簡單易用。不再多說,讓我們開始介紹這些對你來說非常重要的改變。
第一個參數的 label
我們以一個開發者每天都會在 Swift 中用到的例子開始。
?
在函數或方法中的第一個參數現在必須有一個 label ,除非你顯式地聲明不要。以前,我們調用一個函數或方法時,可以忽略第一個參數的 label[SE-0046]:
// old way, Swift 2, followed by new way, 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"作為外部參數名。這是為了增加代碼的可讀性。
如果這個方法不使用介詞也不使用 label,你應該在方法定義時,顯式地在第一個參數名之前加一個下劃線:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { ... } override func didMoveToView(_ view: SKView) { ... }
在許多編程語言中,許多方法可以共用一個方法名,但參數名不同。Swift 也不例外,現在,你可以重載方法,APIs 能夠將直接將它們轉換成合適的調用。下面是一個例子,展示了 index() 方法的兩種重載形式:
let names = ["Anna", "Barbara"] if let annaIndex = names.index(of: "Anna") { print("Barbara's position: \(names.index(after: annaIndex))") }
方法名是同一個,但參數名不同,這將讓人更容易記憶。
省略不必要的單詞
在早期的蘋果標準庫中,方法名中會包含一個單詞,用于表明方法的返回值。因為 Swift 編譯支持類型推斷,這種做法其實并不必要。核心團隊盡可能過濾一切"噪音",因此將這些重復的單詞都刪除了,只留下方法名中最重要的部分。
?
在將 Objective-C 庫轉換成本地 Swift 語言方面,API 變得更智能了[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,GCG 和 Core Graphics 更需要被重新“裝扮一新”。
?
Grand Central Dispatch 常用于長時間計算或者與服務器通訊。將任務放到不同的線程,你可以避免阻塞用戶界面。libdispatch 庫是用 C 語言編寫的,提供了 C 風格的 API。這個 API 現在在 Swift 中被重新設計為[SE-0088]:
// 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()
類似的情況還有 Core Graphics。Core Graphics 是用 C 編寫的,曾經以來一直只能以"丑陋"的函數方式調用。這是它的新的用法[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() }
枚舉中 case 值的大小寫
另一個和過去的 Swift 代碼不同的地方是,在枚舉中定義的 case 值現在使用小駝峰命名法。這是為了和屬性名或者變量名保持一致[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命名法現在只在類型名和協議名上使用。當你習慣這一切之后,Swift團隊對于追求一致性的努力才沒有白費。
返回值的方法或者修改值的方法
標準庫中對方法名中使用動詞和名詞的規定也更加統一。你應當根據這個方法會導致什么后果或者要采取一些動作來進行方法命名。首要原則是如果這個方法名中包含"ed"或"ing"后綴,則表明這是一個名詞。方法名為名詞的方法有返回值。如果不包含這些后綴,則很可能這是一個動詞。以動詞命名的方法會對某塊引用的內存進行一些操作。即所謂的"修改某個值"。下面是幾個符合名詞/動詞命名規則的方法[SE-0006]:
customArray.enumerate() customArray.enumerated() customArray.reverse() customArray.reversed() customArray.sort() // changed from .sortInPlace() customArray.sorted()
下面是一些使用這些方法的代碼片段:
var ages = [21, 10, 2] // 變量,不是常量,這樣你才能修改它 ages.sort() // 修改值,現在值變成了 [2, 10, 21] for (index, age) in ages.enumerated() { // "-ed" 是名詞,表示會返回一個 ages 拷貝 print("\(index). \(age)") // 打印:1. 2 \n 2. 10 \n 3. 21 }
函數類型
函數在聲明和調用時,都需要用括號將參數括住:
func f(a: Int) { ... } f(5)
但是,當你用函數類型作為參數時,你可能會寫出這樣的代碼:
func g(a: Int -> Int) -> Int -> Int { ... } // old way, Swift 2
你會發現代碼很難讀懂。參數在哪里結束,返回值從哪里開始?在 Swift 3 中,正確的定義方法是[SE-0066]:
func g(a: Int -> Int) -> Int -> Int { ... } // old way, Swift 2
現在,參數列表被括號包裹,然后才是返回類型。事情變得簡單,同時函數類型更容易被識別出來。通過下面的比照,你會更清楚:
// 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中新增功能
除了對已有 API 進行“舊瓶新裝”這個最大的改變以外--有非常多的 Swift 社區正致力于此,也有對 Swift API 的一些功能上的增加。
訪問所屬類型
當你定義一個靜態屬性或方法時,你直接通過類或類型來調用它們:
CustomStruct.staticMethod()
如果你當前正在編寫類型內部的代碼,還是要使用類型名來調用靜態方法。為了使表述更加清晰,現在你可以通過 Self 來引用當前實例所屬類型。S 為大寫的 Self 表示引用當前實例的類型,而 s 為小寫的 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
行內Sequences
sequence(first:next:) 以及 sequence(state:next:) 是全局函數,返回一個無限序列。你傳給它們一個初值或一個可變的狀態,它們會在稍后調用閉包中的代碼[SE-0094]:
for view in sequence(first: someView, next: { 0.superview }) { // someView, someView.superview, someView.superview.superview, ... }
還可以用 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 }
雜項
-  keyPath() 等同于 #selector() ,幫助你減少輸入錯誤 
-  你可以在某些類型上調用 pi,比如:Float.pi、CGFloat.pi。大部分時候編譯器能夠推斷出類型:let circumference = 2 * .pi * radius [SE-0067] 
-  NS 前綴從老的 Foundation 類型中移除,現在可以用 Calendar、Date來替代 NSCalendar、NSDate 了 
工具的改善
Swift 只是一門語言,大部分時候你都無法離開書寫它的開發環境--對于蘋果開發者來說,也就是 Xcode!工具上的改變影響著每天編寫代碼的方式。
Swift 3 修正了編譯器和 IDE 中的 Bug,還改善了報告錯誤和信息的精確性。就像你所期望的,每個版本的發布都會讓 Swift 和編譯器的運行變得更快:
-  改善了字符串的 Hash 算法,導致在將字符串存入字典后,性能有 3 倍的提升 
-  將對象從堆移到棧中存放,導致性能有 24 倍的提升(某些情況下) 
-  編譯器可以一次緩存多個文件(在整個模塊被優化過的情況下) 
-  優化了代碼的大小,導致 Swift 代碼的編譯尺寸更小。以蘋果的?Demobots?為例,編譯尺寸縮減了原大小的 77%。 
Xcode 也會更加智能地理解 Swift 代碼:
-  過去,當你在一個 API 方法比如 sort() 上右擊并跳轉到定義時,你會看到一個不太容易理解的頭文件。現在,在 Xcode 8 中,你會看到 sort() 方法實際上是 Array 類的擴展。 
-  Swift Snapshots就如今晚發布的 Swift Evolution 所說。在完全合并到 Xcode 之前,Snapshots 提供了一個使用新語法的機會。Xcode 8 能夠在 playgournd 中加載和運行 Swift Snapshots。 
Swift 包管理器
開源后的 Swift 實際上包含了 Swift 語言本身、核心庫和包管理器。這三者一起構成了我們所見到的 Swift。Swift 包管理器定義了一個簡單的目錄結構,其中包括了你所共享和導入到項目中的 Swift 代碼。
類似于你所用過 Cocoapods 或 Carthage 這些包管理器,Swift 包管理器會下載依賴項并編譯它們,把它們 link 成庫或可執行文件。已經有 1000 個庫支持 Swift 包管理器,并且在未來幾個月中,還會有更多的庫支持它。
計劃中的特性
前面說過,Swift 3 盡量避免不兼容的改變,從而使你的代碼能夠從一個版本與下一個版本兼容。如果這是真的,那么在這次發布中應該還有一些更遠大的目標未能達到,即泛型增強(generic additions)和 ABI(程序二進制接口)穩定。
泛型增強包括遞歸協議約束和能夠將一個受約束的擴展符合新協議(例如,一個用于存放 Equatable 對象的數組也應當遵守 Equatable 協議)。在這些特性尚未實現之前,Swift 不能被視作 ABI 穩定的。
ABI 穩定要求應用程序和庫無論使用哪個版本的編譯器編譯,都能被 link 和能夠彼此進行交互。這對于第三方庫來說是個重要的進步,因為這樣就可以不需要提供源碼就可以封裝出框架了,而現在新版本的 Swift 不但要這些第三方庫修改代碼,還要重新進行編譯。
此外,如果 ABI 穩定就可以移除包含在二進制文件中的 Swift 標準庫,因為就目前來說 iOS 和 macOS APP都是用 Xcode 創建的。目前的二進制文件中包含有 2 MB 左右的文件大小是為了確保能夠兼容未來的操作系統。
總之,你現在只能保持源代碼的版本兼容,而二進制版本的兼容當前仍然是不可能的。
結尾
Swift 仍然在不斷演進,因為社區的目標是最佳實踐。雖然它還很年輕,但它同時也面臨著無比巨大的機遇和美好未來。Swift 現在已經能夠在 Linux 上運行了,在未來幾年,你還會看見它在更多的服務器和設備上運行。從頭設計一門語言必然會破壞 ABI 穩定性,一旦等它穩定后這種破壞就變得小多了。這(開源)是唯一能夠讓這門語言變得更好的機會,否則我們必將后悔莫及。
Swift 正在擴大它的影響力。蘋果正在身體力行。蘋果的團隊們正在 Music app、Console、Sierra畫中畫、Xcode 文檔查看器、新的 iPad 版的Swift Playground app 中使用 Swift。
說到這里,蘋果好像有一種讓非程序員學習 Swift 意圖,例如可以在 iPad 上使用 Swift 以及某些教育措施都表明了這一點。
另外,Swift 正在持續改進:命名變得更規范,代碼讀起來更清晰,你可以用工具來遷移代碼。如果你想進一步深入了解,請看WWDC 的會議視頻。
可以肯定的是,2016年末 Swift 3 的正式發布必將令人期待。我們將持續跟進所有改變,請繼續關注我們的教程、新書預告和視頻,我們將開始使用這些令人興奮的改進。
?
本文轉自:http://www.cocoachina.com/swift/20160712/17028.html
轉載于:https://www.cnblogs.com/xiaopin/p/5861885.html
總結
以上是生活随笔為你收集整理的Swift 3 新特性的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: Data Guard搭建困境突围(一)
- 下一篇: MySQL5.6多实例部署
