Swift 3.0更新內容

轉載自 Raywenderlich What's New in Swift?


Swift 3.0年末就會發布正式版, 并且會給所有Swift開發者帶來很多改變.

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

這篇文章里, 我將會著筆于那些對你的代碼會有重大影響的Swift 3的新特性. 開始吧!

引言##


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

為了讓開發者能夠開始著手移植到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才能體驗到那些新的特性.

我建議嘗試一下我們在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 write

Apple在Xcode 8.0里內置了能夠幫你智能地完成大部分遷移工作的Migration Assistant. 但你還是需要點幾個按鈕去完成這個過程.

你立刻能遷移到swift 2.3或者swift 3. 如果你需要遷移回去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”, “with”和”in”這樣的介詞作為外部參數名( 而不再放在函數或者方法名里). 這一部分的修改是為了提高可讀性.

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

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]
  • 在那些老的Fundation里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的梗), 蘋果的團隊已經在Music, Console, Sierra的畫中畫, Xcode的文檔瀏覽器和新的Swift Playground的iPad版本上用了Swift了.

對于非程序員人群在iPad上學習Swift和通識教育有極大的推動作用.

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容

  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,151評論 4 61
  • 高銘玉閱讀 209評論 0 3
  • 扯淡、節操和A
    AmNobody閱讀 282評論 0 1
  • 大概是完整的看了一次我歌,感覺特別好。老狼和丁武高旗他們出來特別好,聽林子祥rap特別好,coco唱月光愛人的時候...
    啊不二閱讀 687評論 0 0
  • 你有沒有發現,你真的很不愛惜自己?或者,你想愛惜自己,卻不知道該怎么去愛惜?不知道怎么做才是對自己健康有益的...
    可桐閱讀 1,003評論 4 11