文/梁傑_numbbbbb(簡書做者)
原文連接:http://www.jianshu.com/p/2bbe83772f48
著做權歸做者全部,轉載請聯繫做者得到受權,並標註「簡書做者」。
php
Swift 2.2 隨着 iOS 9.3 一同閃亮登場,相較於 Swift 2.1, 2.2 版本作出了許多調整,從其調整方向上咱們也能一窺 Swift 3.0 的影子,如下內容主要來自於蘋果 Swift 官方 Blog,接下來就讓咱們進入正題,一睹 Swift 2.2 的廬山真面目:css
SE-0001: Allow (most) keywords as argument labelsgit
參數標籤是 Swift 中很是 cool 的一個特性,咱們能夠這麼寫:github
for i in 1.stride(to: 9, by: 2) { print(i) }
這個函數很簡單,由 1 開始,每次加 2,返回一系列的值,最後的結果要小於 9:sql
1 3 5 7
上面的函數若是參數前沒有 to
或 by
標籤,即 stride(9, 2)
那麼代碼將失去自解釋性,別人也很難猜到這些參數的實際用途。編程
又假設咱們要獲取集合中某個值對應的索引,能夠聲明以下方法:swift
indexOf(value, in: collection)
可是注意在 Swift 2.2 以前的版本,上面這種寫法 Xcode 會報錯,由於 in 是一個關鍵字,想要使用這些關鍵字必須加上單引號:數組
indexOf(value, `in`: collection)
之前咱們定義新的 API 的時候,一般也要避免與這些關鍵字撞車,好比用 within
代替 in
。在咱們導入 Objective-C APIs 的時候一般會碰到這些問題:ruby
event.touchesMatching([.Began, .Moved], `in`: view)NSXPCInterface(`protocol`: SomeProtocolType.Protocol)
而在 Swift 2.2,咱們開放了除 inout
, var
和 let
之外全部的關鍵字,如今他們均可以做爲參數 label 來使用了(而不用加單引號)關於語法的影響主要注意如下三方面:bash
函數調用中的關鍵字能夠隨意使用了,不會產生什麼歧義,由於方法調用時 ":" 老是伴隨着參數標籤出現。
函數/子類化/初始化 聲明:除 inout
, var
和 let
這三個關鍵字以外,使用其餘關鍵字沒有什麼歧義,由於這些關鍵字後面老是跟隨着 ‘:’
或 ‘_’
好比:
func touchesMatching(phase: NSTouchPhase, in view: NSView?) -> Set<NSTouch>
假如你想在函數聲明中使用 inout
, var
和 let
作爲參數名的話,仍是要加單引號
func addParameter(name: String, `inout`: Bool)
若是在函數類型中這三個關鍵字(inout
,var
,let
)出現的話,是不須要加單引號的,這是由於在這種狀況下參數名後老是跟着 ‘:’
(NSTouchPhase, in: NSView?) -> Set<NSTouch> (String, inout: Bool) -> Void
SE-0015: Tuple comparison operators
元組是以逗號分割的值列表:
let developer = ("Numbbbbb", "Shanks")let designer = ("Cee", "Sai")
之前想要比較兩個元組,咱們須要本身重載操做符
func == (t1: (T, T), t2: (T, T)) -> Bool { return t1.0 == t2.0 && t1.1 == t2.1}
拋開每次都要寫這一坨無趣的代碼不說,並且只能比較包含兩個元素的元組。不過在 Swift 2.2 中,咱們能夠直接比較兩個元組了
let developer = ("Numbbbbb", "Shanks")let designer = ("Cee", "Sai")if developer == designer { print("Matching tuples!") } else { print("Non-matching tuples!") }
Swift 2.2 容許不超過 6 個元素的元組之間進行比較,限制元組的元素個數主要有兩個緣由:
每一次比較都須要在基本庫中添加額外的代碼
元組的元素過多並非一種好的編程風格,考慮用結構體代替
能夠嘗試下面兩個元組比較
let developer = ("Numbbbbb", 23)let designer = ("Cee", "Sai")
不出意外地報錯了:
咱們重點關注下結尾的部分:
note: overloads for '==' exist with these partially matching parameter lists: ...... ((A, B), (A, B)), ((A, B, C), (A, B, C)), ((A, B, C, D), (A, B, C, D)), ((A, B, C, D, E), (A, B, C, D, E)), ((A, B, C, D, E, F), (A, B, C, D, E, F))
Swift 內部函數確實逐字比較了元組的元素,直到 (A, B, C, D, E, F),沒有超過 6 個元素。
SE-0014: Constraining AnySequence.init
AnySequence 表示一個無類型的序列,他遵循 SequenceType
協議,而該協議擁有一個關聯類型 associatedtype SubSequence
,而有時候咱們須要 SubSequence
也要知足 SequenceType
協議
假如咱們有一個 _SequenceBox
internal class _SequenceBox<S : SequenceType> : _AnySequenceBox<S.Generator.Element> { ... }
爲了確保 SubSequence 知足 SequenceType,要作以下限定:
internal class _SequenceBox< S : SequenceType where S.SubSequence : SequenceType, S.SubSequence.Generator.Element == S.Generator.Element, S.SubSequence.SubSequence == S.SubSequence> : _AnySequenceBox<S.Generator.Element> { ... }
反過來,他也會影響 AnySequence.init
作一些限定:
修改前的 AnySequence.init
:
public struct AnySequence<Element> : SequenceType { public init< S: SequenceType where S.Generator.Element == Element >(_ base: S) { ... } }
修改後的 AnySequence.init
:
public struct AnySequence<Element> : SequenceType { public init< S: SequenceType where S.Generator.Element == Element, S.SubSequence : SequenceType, S.SubSequence.Generator.Element == Element, S.SubSequence.SubSequence == S.SubSequence >(_ base: S) { ... } }
事實上,這些約束應該被應用到 SequenceType
協議自身上(儘管就目前來看是不太可能了),同咱們預期的那樣每一個 SequenceType
實現都已經自我知足。
SE-0011: Replace typealias keyword with associatedtype for associated type declarations
在 Swift 2.2 之前的版本中關鍵字 typealias
能夠用來聲明兩種類型
類型別名(爲已存在的類型起一個別名)
關聯類型(做爲佔位符類型成爲協議的一部分)
以上兩種聲明應該使用不一樣的關鍵字,爲此咱們爲關聯類型準備了新的關鍵字 associatedtype
,所以在 Swift 2.2 中 typealias
只能用作類型別名的聲明,因此協議中使用的關聯類型只能用 associatedtype
,若是用了 typealias
就會報錯:
protocol Prot { associatedtype Container : SequenceType typealias Element = Container.Generator.Element // error: cannot declare type alias inside protocol, use protocol extension instead }
應將 typealias
移到 extension 中去
protocol Prot { associatedtype Container : SequenceType}extension Prot { typealias Element = Container.Generator.Element}
SE-0021: Naming Functions with Argument Labels
由於在 Swift 中,函數是一等公民,因此函數能夠賦值給變量,當作普通值傳遞。爲此咱們須要一個函數類型來對該變量作限定。一般咱們會使用函數名做爲主要類型部分,可是有許多基本名字相同的函數,僅僅是參數或參數標籤不一樣而已,好比 UIView
:
extension UIView { func insertSubview(view: UIView, at index: Int) func insertSubview(view: UIView, aboveSubview siblingSubview: UIView) func insertSubview(view: UIView, belowSubview siblingSubview: UIView)}
咱們調用時也是經過參數標籤來區分不一樣的方法:
someView.insertSubview(view, at: 3) someView.insertSubview(view, aboveSubview: otherView) someView.insertSubview(view, belowSubview: otherView)
可是,當咱們建立一個函數的引用時,就會產生一個歧義,即沒法肯定調用的是 UIView 的哪一個方法
let fn = someView.insertSubview // ambiguous: could be any of the three methods
咱們可使用類型註解來消除歧義
let fn: (UIView, Int) = someView.insertSubview // ok: uses insertSubview(_:at:)let fn: (UIView, UIView) = someView.insertSubview // error: still ambiguous!
可是上面的代碼後者由於 (UIView, UIView) 存在兩個方法(aboveSubview 和 belowSubview),因此仍是存在歧義,只能用閉包的方式來指名傳遞的方法:
let fn: (UIView, UIView) = { view, otherView in button.insertSubview(view, aboveSubview: otherView) }
這樣作法太乏味了,Swift 2.2 如今容許咱們將函數命名爲:函數名 + 參數標籤的組合來消除歧義:
let fn = someView.insertSubview(_:at:)let fn1 = someView.insertSubview(_:aboveSubview:)
一樣的語法也能夠用作初始化的引用:
let buttonFactory = UIButton.init(type:)
爲指定的方法生成一個 Objective-C 選擇器:
let getter = Selector(NSDictionary.insertSubview(_:aboveSubview:)) // produces insertSubview:aboveSubview:.
SE-0022: Referencing the Objective-C selector of a method
在 Swift 2,Objective-C selectors 一般會根據其字面值寫成字符串常量,好比 "insertSubview:aboveSubview:"
這樣比較容易出錯,例以下面的:
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Tap!", style: .Plain, target: self, action: "buttonTaped")
若是你眼神夠好,會發現我把 buttonTapped
寫成了 buttonTaped
,但 Xcode 也不會給我報錯。這一切在 Swift 2.2 終於獲得解決,字符串做爲 selector 被 deprecated 了,從此該這麼寫 #selector(buttonTapped)
,這樣發生拼寫錯誤,也能及時獲得編譯器的提醒。
即便在純 Swift 環境中(徹底與 Objective-C 徹底無關),咱們也能夠經過 #selector(Swift 方法名) 的方式來實現 Swift 的 selector
control.sendAction(#selector(MyApplication.doSomething), to: target, forEvent: event)extension MyApplication { @objc(jumpUpAndDown:) func doSomething(sender: AnyObject?) { … } }
建立一個 Selector 的引用
let sel = #selector(UIView.insertSubview(_:at:)) // produces the Selector "insertSubview:atIndex:"
SE-0020: Swift Language Version Build Configuration
在大部分時候,隨着 Swift 版本更新語法也會有較大調整,可是第三方類庫的維護者們但願他們的庫可以同時兼容不一樣版本的 Swift,目前可行的辦法是同時維護多個分支來支持不一樣版本的語言。
Swift 2.2 提供了新的選項使你將同一個版本的 Swift 代碼集中在同一個文件中,而編譯器會在編譯時選擇具體的 Swift 版原本執行
#if swift(>=2.2)print("Running Swift 2.2 or later")#elseprint("Running Swift 2.1 or earlier")#endif
相似於現存的 #if os()
構建選項,這個選項決定了編譯器隨後生成的代碼,若是你使用的是 Swift 2.2,那麼第二個 print()
將不會被看到。
蘋果 Swift 官方 Blog 沒有提到的 Swift 2.2 一些新特性
Swift 2.2 正式將 ++
和 --
deprecates 掉了,意味着雖然在 Swift 2.2 版本還能工做,但編譯器會給你一個警告。但在 3.0 版本會被徹底移除。
你可使用 += 1 和 -= 1 來替代,至於爲何要將其移除,有這麼幾個解釋:
寫 ++ 並不比 +=1 能節省多少時間
++ 對學 Swift 沒有任何幫助,+= 至少可讀性更好
傳統 C styel for 循環中的 -- 也被 deprecated 了
也就是說下面這種寫法在 2.2 的版本被 deprecates 了
for var i = 1; i <= 10; i += 1 { print("\(i) SwiftGG awesome") }
之後要這麼寫了:
for i in 1...10 { print("\(i) SwiftGG awesome") }
若是想要建立一個由大到小的範圍,你按照下面的寫法編譯或許沒問題,但運行時會崩潰
for i in 10...1 { print("\(i) SwiftGG awesome") }
應當這麼寫:
for i in (1...10).reverse() { print("\(i) SwiftGG awesome") }
另外一種選擇是使用標準的快速枚舉來遍歷數組
var array = Array(1...10)for number in array { print("\(number) green bottles") }
Swift 2.2 終於爲咱們帶來了 removeFirst() 方法,該方法將從數組中移除第一個元素,而後返回給咱們,能夠試驗一下
var array = Array(1...5)array.removeFirst()for number in array { print("the \(number) bird") }
使用
removeLast()
時要注意,若是是空數組,會崩潰,所以能夠用popLast()
來替代,該方法會處理空數組的情形(返回 nil)
們能夠用下面的方式定義一個函數,在 Swift 2.2 以前能夠有兩種方式調用
func foo(a : Int, b : Int) {}
第一種咱們常用,爲函數的每一個參數都傳遞相對應的值
foo(42, b : 17)
或者咱們能夠利用一個大部分開發者都不那麼熟悉的特性(tuple splat)來調用
let x = (1, b: 2)foo(x)
後者這種語法糖實在沒什麼實際意義,在 Swift 2.2 被 deprecated,將在將來的版本移除。
var 參數提供的益處微乎其微,並且容易讓人與 inout 混淆,所以在 Swift 2.2 中被移除了。
舉個例子 sayHello() 函數使用了 var 參數:
func sayHello(var name: String, repeat repeatCount: Int) { name = name.uppercaseString for _ in 0 ..< repeatCount { print(name) } } sayHello("numbbbbb", repeat: 5)
結果是 NUMBBBBB 將會被打印 5 遍,這是由於參數 name 經 var 修飾後成爲變量,而後執行 uppercaseString
方法轉換爲大寫,若是沒有 var
關鍵字,name 是常量,執行 uppercaseString
會失敗。
var 和 inout 之間的差別很是微妙:
使用 var,讓你能夠在函數內部修改參數
使用 inout,甚至可讓你的改變延續到函數結束後
咱們能夠在 Swift 2.2 中這麼寫:
func sayHello(name: String, repeat repeatCount: Int) { let upperName = name.uppercaseString for _ in 0 ..< repeatCount { print(upperName) } } sayHello("numbbbbb ", repeat: 5)
在 Swift 2.1 和以前的版本,使用 __FILE__
, __LINE__
, __COLUMN__
, 和 __FUNCTION__
標識符,在編譯時會被替換爲文件名、行號、列號和函數名。
而在 Swift 2.2 這些舊的標識符被更新爲 #file
, #line
, #column
和 #function
,若是你以前使用過 Swift 2.0 的 #available
來檢查 iOS 版本,正如官方所說 # 意味這編譯器這裏要執行替換邏輯。
下面在 printGreeting()
函數中演示了新舊兩種 debug 標識符:
func sayHello(name: String, repeat repeatCount: Int) { // old - deprecated! print("This is on line \(__LINE__) of \(__FUNCTION__)") // new - shiny! print("This is on line \(#line) of \(#function)") let upperName = name.uppercaseString for _ in 0 ..< repeatCount { print(upperName) } } sayHello("numbbbbb", repeat: 5)