
原文:What’s New in Swift 3
作者:COSMIN PUPĂZĂ
译者:kmyhy
在 WWDC 大會上,蘋果在 Xcode 8 beta 中集成了 Swift 3,最後的版本則需要到年末的時候才會放出。這是 Swift 開源以後第一個版本,它將同時支持 Mac OS X 和 linux。如果你關注過去年 11 月份開始的 Swift Evolution 專案,它甚至已經可以在 IBM sandbox 上運行了,這門語言真的有了天翻地覆的變化。如果你用 Xcode 8 編譯你的專案,你會發現根本無法編譯了。
Swift 3 的改進主要集中在兩個方面:
刪除在 Swift 2.2 中聲明的“已拋棄”特性 讓語言更加“先進”讓我們先從简单的开始,即 Swift 3 中已刪除的語法特性。這些特性是你在 Xcode 7.3 的中曾經看到過的一些警告。
++ 和 – 運算子自增、自減運算是從 C 語言中繼承來的,它們的功能很簡單:在某個變量的基礎上減去或加上 1。
var i = 0i++++ii----i 但是,当要选择用这些運算中的哪一個時,往往讓人不知所措。自增和自減運算子都會有兩種使用的方式:前置或後置 ── 它們的實現完全隱藏在底層,返回值對你來說可能有用,也可能無用,幸好我們還有運算子重載。
對於初學者來說,它們太難控制了,因此現在將它們移除了 ── 現在它們完全被 += 和 ﹣= 所替代了:
var i = 0i += 1i -= 1 雖然復合賦值運算要簡短一些,但只要你願意,你也可以用 + 和 ﹣ 運算来達到同樣的目的 :
i = i + 1i = i - 1 [ecko_alert color=”gray”]編後語: 如果你想了解這项改進的最初提議,你可以閱讀Chris Lattner 的關於移除 ++/– 運算子的提議。[/ecko_alert]
C 語言風格的 for 循環成為歷史對於自增、自減運算用得最多的地方就是 C 語言的經典循環了。當自增、自減運算子被移除后,同時也意味著 C 經典循環的歷史使命被終結了,因為經典循環能做的,用 for-in 循環結合區間的使用同樣能夠做到。
如果你有一定的編程經驗,那麼打印從 1 到 10 的數字你可以用 for 循環來實現:
for (i = 1; i <= 10; i++) { print(i) }
在 Swift 3 中,不再允許這樣做了。下面是 Swift 3 的同一功能的實現 ── 注意,封閉區間運算子 … 的使用:
for i in 1...10 { print(i)} 此外,你也可以使用 for each 循環和閉包、快捷參數來實現同樣的目的 ── 關於循環的更多討論,請參考這裡。
(1...10).forEach { print(func gcd(var a: Int,var b: Int) -> Int { if (a == b) { return a } repeat { if (a > b) { a = a - b } else { b = b - a } } while (a != b) return a})} [ecko_alert color=”gray”]編後語: 如果你想了解這項改進的最初提議,請閱讀 Erica Sadun 的關於 C 語言風格的 for 循環的提議 。[/ecko_alert]
函數參數中的 var 被移除函數參數一般是當做常量來用的,因為在方法中你通常不會修改它們的值。但是某些情況下,你需要將它們聲明為變量。在 Swift 2 中,你可以用 var 來修飾一個函數參數。一旦參數被 var 所修飾,它會創建一份局部變量的拷貝,因此你可以在函數中修改它的值。
例如,下面的函數判斷兩個數字中的最大公約數 ── 如果你忘記了你的高中數學,請你先看這裡:
func gcd(a: Int,b: Int) -> Int { if (a == b) { return a } var c = a var d = b repeat { if (c > d) { c = c - d } else { d = d - c } } while (c != d) return c} 算法很簡單:如果兩個數字相等,隨便返回其中一個。否則,對二者進行比較,用大的一個減去小的一個并將結果賦給大的一個,直到二者完全相等,最終返回二者之一。如你所見,a 和 b 都用 var 修飾了,因此在函數中我們可以任意改變二者的值。
Swift 3 不再允許這樣做,因為這樣會讓開發者將 var 和 inout 混淆起來。因此新版本中直接不允許在函數參數中使用 var。
因此,gcd 函數需要在 Swift 3 中用其它方式實現。你需要將參數值保存到一個局部變量中:
gcd(8,b: 12) 如果你想了解這項改進的最初提議,請閱讀原始提議。
對函數的參數名一視同仁函數的參數列表,底層上實際是以元組(tuple)表示的,因此你可以以元組的方式調用函數,就好像調用一個和函數原型相同的元組。以 gcd() 函數為例,你可以這樣調用它:
let number = (8,b: 12)gcd(number) 也可以這樣調用它:
gcd(a: 8,b: 12) 如你所見,在 Swift 2 中第一個參數的 Label(又叫外部參數名)不是必須的。但是,第二個參數的參數名(以及剩餘的其它參數名)則是必須的。
這種語法也會讓新手茫然,因此這次對參數名的使用進行了規範化。在 Swift 3 中,你必須這樣調用這個函數:
func gcd(_ a: Int,b: Int) -> Int {...} 必須顯式地指定第一個參數名。否則,Xcode 8 會提示錯誤。
對此你的第一反應可能是“上帝!這得給我的程式碼帶來多大的修改量!”。你說對了,這個修改量還真不小。因此蘋果提供了一個調用函數時忽略第一個參數名的方法。你可以在第一個參數前面加一個下劃線:
// 1import UIKitimport XCPlayground// 2class Responder: NSObject { func tap() { print("button pressed") }}let responder = Responder()// 3let button = UIbutton(type: .System)button.setTitle("button",forState: .normal)button.addTarget(responder,action: "tap",forControlEvents: .touchUpInsIDe)button.sizetoFit()button.center = CGPoint(x: 50,y: 25)// 4let frame = CGRect(x: 0,y: 0,wIDth: 100,height: 50)let vIEw = UIVIEw(frame: frame)vIEw.addSubvIEw(button)XCPlaygroundPage.currentPage.liveVIEw = vIEw 這樣,你可以不用修改原來的函數調用方式 ── 即不用書寫第一個參數名。這會簡少將程式碼從 Swift 2 升級到 Swift 3 的工作量。
[ecko_alert color=”gray”]編後語: 要了解這項改進的最初提議,請閱讀這個提議。[/ecko_alert]
不能用字符串表示 Selector讓我們創建一顆按鈕,讓它被觸摸時執行某些動作 ── 假設只能用 playground 而不能用 Interface Builder 實現的話,這段程式碼應當編寫成:
#selector() 程式碼有點多,讓我們將它分成幾步來進行說明:
導入 UIKit 和 XCPlayground 框架 ── 這樣我們才能創建按鈕并將它顯示到 playground 的助手編輯器中。
注意: 通過 VIEw -> Assistant Editor -> Show Assistant Editor 菜單,你可以打開助手編輯器并與按鈕進行交互。
定義一個 tap 方法,這個方法會在用戶觸摸按鈕時觸發,并將一個 responder 對象傳遞給按鈕的 target ── responder 必須繼承自 NSObject,因為 selector 對象只對 Objective-C 方法有效。
創建一個按鈕并設置其屬性。
創建一個視圖,初始化時指定其 frame,然後將按鈕加到 vIEw 上,然後在 playground 的助手編輯器上顯示。
注意突出顯示的程式碼。按鈕的 selector 用一個字符串指定。如果你將這個寫錯了,程式碼仍然能夠編譯,但在運行時會因為找不到相應的方法而崩潰。
為了在編譯時解決這種潛伏的問題,Swift 3 使用
button.addTarget(responder,action: #selector(Responder.tap),for: .touchUpInsIDe) 關鍵字來表示 selector。這樣,如果你寫錯了方法名,編譯器就會提前檢查出問題所在。 class Person: NSObject { var name: String = "" init(name: String) { self.name = name }}let me = Person(name: "Cosmin")me.valueForKeyPath("name") [ecko_alert color=”gray”]編後語: 要了解這項改進的最初提議,請閱讀 Doug Gregor 的提議。[/ecko_alert]
上面就是 Swift 中已刪除的語法特性。現在讓我們看一下讓 Swift 更加具有“先進性”的改進有哪些。
Key-paths 不再使用字符串這個特性跟前一個有點類似,但它被用於 kvc(鍵值編碼)和 kvo(鍵值觀察)。
#keyPath() 你創建了一個 Person 類,它是鍵值編碼兼容的,在指定的初始化方法中傳入了我的名字,然後通過 key-path 來讀取我的名字。同樣,如果你把 key-path 弄錯了,app 會崩潰,我會很生氣!:(
幸運的是,Swift 3 解決了這個問題。key-path 字符串現在需要用
class Person: NSObject { var name: String = "" init(name: String) { self.name = name }}let me = Person(name: "Cosmin")me.value(forKeyPath: #keyPath(Person.name)) 表達式來替換: let file = NSBundle.mainBundle().pathForResource("tutorials",ofType: "Json")let url = NSURL(fileURLWithPath: file!)let data = NSData(contentsOfURL: url)let Json = try! NSJsONSerialization.JsONObjectWithData(data!,options: [])print(Json) [ecko_alert color=”gray”]編後語: 關於這個改進的最初提議,你可以查看 David Hart 的提議。[/ecko_alert]
Foundation 類型名不再需要 NS 前綴NS 前綴終於從 Foundation 類型名中移除了 ── 如果你想知道這項改進的最初提議,請看這裡。典型的例子如 JsON 解析:
let file = Bundle.main().pathForResource("tutorials",ofType: "Json")let url = URL(fileURLWithPath: file!)let data = try! Data(contentsOf: url)let Json = try! JsONSerialization.JsonObject(with: data)print(Json) 在將 JsON 數據從文件中讀出的過程中,我們使用了幾個 Foundation 類:
NSBundle -> NSURL -> NSData -> NSJsONSerialization
在 Swift 3 中,NS 前綴被移除了,因此上面的解析過程就變成了:
Bundle -> URL -> Data -> JsONSerialization:
let r = 3.0let circumference = 2 * M_PI * rlet area = M_PI * r * r [ecko_alert color=”gray”]編後語: 要想知道這項改進的最初提議,請查看 Tony Parker 和 Philippe Hausler 的 這個提議 [/ecko_alert]
M_PI 和 .pi讓我們用圓半徑算出圓的周長和面積:
float.piDouble.piCGfloat.pi 在老的 Swift 版本中,M_PI 代表了圓周率。Swift 3 中,則將 pi 放到了 float,Double 和 CGfloat 類型的內部:
let r = 3.0let circumference = 2 * Double.pi * rlet area = Double.pi * r * r 因此前面的(計算圓周長和面積)的程式碼,用 Swift 3 則可以編寫為:
let r = 3.0let circumference = 2 * .pi * rlet area = .pi * r * r 由於類型推斷的存在,我們甚至可以忽略類型名,因此代碼可以簡化為:
let queue = dispatch_queue_create("Swift 2.2",nil)dispatch_async(queue) { print("Swift 2.2 queue")} GCD GCD(Grand Central dispath) 通常被用於執行網絡 *** 作而不阻塞主線程中的用戶界面。它是用 C 編寫的,因此它的 API 對於初學者來說難於理解,尤其是想在異步隊列中做某些工作的時候:
let queue = dispatchQueue(label: "Swift 3")queue.async { print("Swift 3 queue")} Swift 3 刪除了這些公式化和繁瑣的語法,改用面向對象的方法來實現:
let frame = CGRect(x: 0,height: 50)class VIEw: UIVIEw { overrIDe func drawRect(rect: CGRect) { let context = UIGraphicsGetCurrentContext() let blue = UIcolor.bluecolor().CGcolor CGContextSetFillcolorWithcolor(context,blue) let red = UIcolor.redcolor().CGcolor CGContextSetstrokecolorWithcolor(context,red) CGContextSetlinewidth(context,10) CGContextAddRect(context,frame) CGContextDrawPath(context,.Fillstroke) }}let aVIEw = VIEw(frame: frame) [ecko_alert color=”gray”]編後語: 要了解這項改進的最初提議,請閱讀 Matt Wright 的這個提議 [/ecko_alert]
Core Graphics 變得更加“Swift 化”Core Graphics 是一個強大的圖形框架,但使用了跟 GCD 一樣 C 風格的 API:
let frame = CGRect(x: 0,height: 50)class VIEw: UIVIEw { overrIDe func draw(_ rect: CGRect) { guard let context = UIGraphicsGetCurrentContext() else { return } let blue = UIcolor.blue().cgcolor context.setFillcolor(blue) let red = UIcolor.red().cgcolor context.setstrokecolor(red) context.setlinewidth(10) context.addRect(frame) context.drawPath(using: .fillstroke) }}let aVIEw = VIEw(frame: frame) 你需要先指定 vIEw 的 frame,繼承 UIVIEw 類,重寫 drawRect 方法以進行定制的繪圖 *** 作,使 vIEw 呈現不一樣的畫面。
Swift 3 中,使用了一種全新的方法來使用 Core Graphic ── 先獲取前繪圖上下文,將它進行解包,解包到一個 context 對象,然後再通過這個 context 來執行所有的繪圖 *** 作:
for i in (1...10).reverse() { print(i)} 注意: 在 vIEw 調用它的 drawRect 方法之前,圖形上下文為空,因此你需要對其用 guard 語句進行解包 *** 作 ── 更多內容請閱讀這裡。
動詞/名詞命名規範接下來該講一點語法了!:) Swift 3 將方法分成兩類:有返回值的 ── 使用名詞命名,以及執行某種 *** 作的 ── 使用動詞命名。
下面打印從 10 到 1 的數字:
for i in (1...10).reversed() { print(i)} reverse() 方法用於將區間反序。在 Swift 3 看來,這個方法應該用名詞,因為它將原來的區間反向排序后返回。因此,它在這個方法後面加了一個 ed 後綴:
var array = [1,5,3,2,4]for (index,value) in array.enumerate() { print("\(index + 1) \(value)")} 元組最常用的用法之一,就是打印一個數組的內容:
var array = [1,value) in array.enumerated() { print("\(index + 1) \(value)")} 在 Swift 3 中,這個方法應該用名詞,因為它也返回了一個由當前數組元素的索引和值共同組成的元組。因此它在這個方法後面也加了一個 ed 後綴:
var array = [1,4]let sortedArray = array.sort()print(sortedArray) 另一個例子是數組的排序。下面我們將一個數組按照升序進行排序:
var array = [1,4]let sortedArray = array.sorted()print(sortedArray) 在 Swift 3 中,這個方法應該使用名詞,因為它返回了經過排序的數組。因此 sort 方法現在被命名為 sorted 方法:
var array = [1,4]array.sortInPlace()print(array) 讓我們直接在數組上進行 *** 作,去掉中間變量。在 Swift 2 中,你會用這樣的程式碼:
var array = [1,4]array.sort()print(array) 你通過 sortInPlace() 方法對一個可變數組排序。Swift 3 中,這個方法名應該用動詞,因為它直接進行實際上的排序 *** 作,而不返回任何東西。只需要用最簡單的單詞就可以描述這種動作。因此 sortInPlace() 應該被 sort() 代替:
XCPlaygroundPage.currentPage [ecko_alert color=”gray”]編後語: 關於這項命名規範的最初提議,請閱讀 API 設計指南。[/ecko_alert]
API 更加“swift 化”Swift 3 使用了一個簡單的哲學來規範其 API ── 刪除無用的單詞,如果某個單詞是多餘的或者是能夠通過上下文推斷出來的,則刪除它:
PlaygroundPage.current 改為 button.setTitle(forState) button.setTitle(for) 改為 button.addTarget(action,forControlEvents) button.addTarget(action,for) 改為 NSBundle.mainBundle() Bundle.main() 改為 NSData(contentsOfURL) URL(contentsOf) 改為 NSJsONSerialization.JsONObjectWithData() JsONSerialization.JsonObject(with) 改為 UIcolor.bluecolor() UIcolor.blue() 改為 UIcolor.redcolor() UIcolor.red() 改為 .System 枚舉值 Swift 3 將枚舉值看成屬性,因此使用“小駝峰法”而不是“大駝峰法”進行命名:
.system 改為 .touchUpInsIDe .touchUpInsIDe 改為 .Fillstroke .fillstroke 改為 .CGcolor .cgcolor 改為 overrIDe func vIEwDIDLoad() { super.vIEwDIDLoad() printMessage(message: "Hello Swift 3!")}@discardableResultfunc printMessage(message: String) -> String { let outputMessage = "Output : \(message)" print(outputMessage) return outputMessage} @discardableResult 在 Swift 3 中,如果你不使用方法或函數的返回值,Xcode 會顯示警告,例如:
在上面的程式碼中,printMessage 方法返回一個字符串。但是,這個返回值在我們調用方法的時候沒有用到,這可能引發潛在的問題,因此 Swift 3 編譯器會發出警告。
但是在某些情況下,我們實在是沒有必要保留返回值,因此你可以用一個 @discardableResult 表明方法不需要這種警告:
結束關於 Swift 3 就介紹到這裡。新的 Swift 版本的發佈,讓這門語言越來越好用啦。在它包含了大量的功能改進的同時,也會對你現在的代碼產生非常大的影響。我希望這篇教程能讓你更好地理解這些改進,同時節省你升級 Swift 專案的時間成本。
本教程中的所有程式碼都可以在 這裡 下載。我已經在 Xcode 8 beta 中通過了測試。請確保你在 Xcode 8 中運行這個專案。
如果你有任何問題和建議,請告訴我。祝你 Coding 快樂! :)
譯者簡介楊宏焱,男,中國大陸籍人士,CSDN 博客專家(個人博客 http://blog.csdn.net/kmyhy)。2009 年開始學習蘋果 iOS 開發,精通 O-C/Swift 和 Cocoa touch 框架,開發有多個商店應用和企業 App。熱愛寫作,著有和翻譯有多本技術專著,包括:《企業級 iOS 應用實戰》、《iPhone & iPad 企業移動應用開發秘笈》、《iOS8 Swift 編程指南》,《寫給大忙人看的 Swift》(合作翻譯)、《iOS Swift game Development cookbook》等。
总结以上是内存溢出为你收集整理的What's new in Swift 3全部内容,希望文章能够帮你解决What's new in Swift 3所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)