swift2.2的新特性
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
文/梁杰_numbbbbb(簡(jiǎn)書(shū)作者)
原文鏈接:http://www.jianshu.com/p/2bbe83772f48
著作權(quán)歸作者所有,轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),并標(biāo)注“簡(jiǎn)書(shū)作者”。
Swift 2.2 隨著 iOS 9.3 一同閃亮登場(chǎng),相較于 Swift 2.1, 2.2 版本做出了許多調(diào)整,從其調(diào)整方向上我們也能一窺 Swift 3.0 的影子,以下內(nèi)容主要來(lái)自于蘋(píng)果 Swift 官方 Blog,接下來(lái)就讓我們進(jìn)入正題,一睹 Swift 2.2 的廬山真面目:
允許更多的關(guān)鍵字來(lái)做參數(shù)標(biāo)簽
SE-0001: Allow (most) keywords as argument labels
參數(shù)標(biāo)簽是 Swift 中非常 cool 的一個(gè)特性,我們可以這么寫(xiě):
for?i?in?1.stride(to:?9,?by:?2)?{print(i) }這個(gè)函數(shù)很簡(jiǎn)單,由 1 開(kāi)始,每次加 2,返回一系列的值,最后的結(jié)果要小于 9:
1?3?5?7上面的函數(shù)如果參數(shù)前沒(méi)有 to 或 by 標(biāo)簽,即 stride(9, 2) 那么代碼將失去自解釋性,別人也很難猜到這些參數(shù)的實(shí)際用途。
又假設(shè)我們要獲取集合中某個(gè)值對(duì)應(yīng)的索引,可以聲明如下方法:
indexOf(value,?in:?collection)但是注意在 Swift 2.2 之前的版本,上面這種寫(xiě)法 Xcode 會(huì)報(bào)錯(cuò),因?yàn)?in 是一個(gè)關(guān)鍵字,想要使用這些關(guān)鍵字必須加上單引號(hào):
indexOf(value,?`in`:?collection)以前我們定義新的 API 的時(shí)候,通常也要避免與這些關(guān)鍵字撞車(chē),比如用 within 代替 in。在我們導(dǎo)入 Objective-C APIs 的時(shí)候通常會(huì)碰到這些問(wèn)題:
event.touchesMatching([.Began,?.Moved],?`in`:?view)NSXPCInterface(`protocol`:?SomeProtocolType.Protocol)而在 Swift 2.2,我們開(kāi)放了除 inout, var 和 let 以外所有的關(guān)鍵字,現(xiàn)在他們都可以作為參數(shù) label 來(lái)使用了(而不用加單引號(hào))關(guān)于語(yǔ)法的影響主要注意以下三方面:
函數(shù)調(diào)用中的關(guān)鍵字可以隨意使用了,不會(huì)產(chǎn)生什么歧義,因?yàn)榉椒ㄕ{(diào)用時(shí) ":" 總是伴隨著參數(shù)標(biāo)簽出現(xiàn)。
函數(shù)/子類(lèi)化/初始化 聲明:除 inout, var 和 let 這三個(gè)關(guān)鍵字之外,使用其他關(guān)鍵字沒(méi)有什么歧義,因?yàn)檫@些關(guān)鍵字后面總是跟隨著 ‘:’ 或 ‘_’ 比如:
假如你想在函數(shù)聲明中使用 inout, var 和 let 做為參數(shù)名的話(huà),還是要加單引號(hào)
????func?addParameter(name:?String,?`inout`:?Bool)如果在函數(shù)類(lèi)型中這三個(gè)關(guān)鍵字(inout,var,let)出現(xiàn)的話(huà),是不需要加單引號(hào)的,這是因?yàn)樵谶@種情況下參數(shù)名后總是跟著 ‘:’
元組對(duì)象可以進(jìn)行比較操作了
SE-0015: Tuple comparison operators
元組是以逗號(hào)分割的值列表:
let?developer?=?("Numbbbbb",?"Shanks")let?designer?=?("Cee",?"Sai")以前想要比較兩個(gè)元組,我們需要自己重載操作符
func?==??(t1:?(T,?T),?t2:?(T,?T))?->?Bool?{????return?t1.0?==?t2.0?&&?t1.1?==?t2.1}拋開(kāi)每次都要寫(xiě)這一坨無(wú)趣的代碼不說(shuō),而且只能比較包含兩個(gè)元素的元組。不過(guò)在 Swift 2.2 中,我們可以直接比較兩個(gè)元組了
let?developer?=?("Numbbbbb",?"Shanks")let?designer?=?("Cee",?"Sai")if?developer?==?designer?{????print("Matching?tuples!") }?else?{????print("Non-matching?tuples!") }Swift 2.2 允許不超過(guò) 6 個(gè)元素的元組之間進(jìn)行比較,限制元組的元素個(gè)數(shù)主要有兩個(gè)原因:
每一次比較都需要在基本庫(kù)中添加額外的代碼
元組的元素過(guò)多并不是一種好的編程風(fēng)格,考慮用結(jié)構(gòu)體代替
可以嘗試下面兩個(gè)元組比較
let?developer?=?("Numbbbbb",?23)let?designer?=?("Cee",?"Sai")不出意外地報(bào)錯(cuò)了:
我們重點(diǎn)關(guān)注下結(jié)尾的部分:
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 內(nèi)部函數(shù)確實(shí)逐字比較了元組的元素,直到 (A, B, C, D, E, F),沒(méi)有超過(guò) 6 個(gè)元素。
為 AnySequence.init 增加約束條件
SE-0014: Constraining AnySequence.init
AnySequence 表示一個(gè)無(wú)類(lèi)型的序列,他遵循 SequenceType 協(xié)議,而該協(xié)議擁有一個(gè)關(guān)聯(lián)類(lèi)型 associatedtype SubSequence ,而有時(shí)候我們需要 SubSequence 也要滿(mǎn)足 SequenceType 協(xié)議
假如我們有一個(gè) _SequenceBox
internal?class?_SequenceBox<S?:?SequenceType>:?_AnySequenceBox<S.Generator.Element>?{?...?}為了確保 SubSequence 滿(mǎn)足 SequenceType,要做如下限定:
internal?class?_SequenceBox<??S?:?SequenceTypewhereS.SubSequence?:?SequenceType,????S.SubSequence.Generator.Element?==?S.Generator.Element,????S.SubSequence.SubSequence?==?S.SubSequence>?:?_AnySequenceBox<S.Generator.Element>?{?...?}反過(guò)來(lái),他也會(huì)影響 AnySequence.init 做一些限定:
修改前的 AnySequence.init:
public?struct?AnySequence<Element>?:?SequenceType?{??public?init<S:?SequenceType????whereS.Generator.Element?==?Element>(_?base:?S)?{?...?} }修改后的 AnySequence.init:
public?struct?AnySequence<Element>?:?SequenceType?{public?init<????S:?SequenceTypewhereS.Generator.Element?==?Element,??????S.SubSequence?:?SequenceType,??????S.SubSequence.Generator.Element?==?Element,??????S.SubSequence.SubSequence?==?S.SubSequence>(_?base:?S)?{?...?} }事實(shí)上,這些約束應(yīng)該被應(yīng)用到 SequenceType 協(xié)議自身上(盡管就目前來(lái)看是不太可能了),同我們預(yù)期的那樣每個(gè) SequenceType 實(shí)現(xiàn)都已經(jīng)自我滿(mǎn)足。
在聲明相關(guān)類(lèi)型時(shí)用 associatedtype 來(lái)替換 typealias
SE-0011: Replace typealias keyword with associatedtype for associated type declarations
在 Swift 2.2 以前的版本中關(guān)鍵字 typealias 可以用來(lái)聲明兩種類(lèi)型
類(lèi)型別名(為已存在的類(lèi)型起一個(gè)別名)
關(guān)聯(lián)類(lèi)型(作為占位符類(lèi)型成為協(xié)議的一部分)
以上兩種聲明應(yīng)該使用不同的關(guān)鍵字,為此我們?yōu)殛P(guān)聯(lián)類(lèi)型準(zhǔn)備了新的關(guān)鍵字 associatedtype,因此在 Swift 2.2 中 typealias 只能用做類(lèi)型別名的聲明,所以協(xié)議中使用的關(guān)聯(lián)類(lèi)型只能用 associatedtype,如果用了 typealias 就會(huì)報(bào)錯(cuò):
protocol?Prot?{associatedtype?Container?:?SequenceTypetypealias?Element?=?Container.Generator.Element?//?error:?cannot?declare?type?alias?inside?protocol,?use?protocol?extension?instead }應(yīng)將 typealias 移到 extension 中去
protocol?Prot?{associatedtype?Container?:?SequenceType}extension?Prot?{????typealias?Element?=?Container.Generator.Element}命名函數(shù)時(shí)帶上參數(shù)標(biāo)簽
SE-0021: Naming Functions with Argument Labels
因?yàn)樵?Swift 中,函數(shù)是一等公民,所以函數(shù)可以賦值給變量,當(dāng)做普通值傳遞。為此我們需要一個(gè)函數(shù)類(lèi)型來(lái)對(duì)該變量做限定。通常我們會(huì)使用函數(shù)名作為主要類(lèi)型部分,但是有許多基本名字相同的函數(shù),僅僅是參數(shù)或參數(shù)標(biāo)簽不同而已,比如 UIView:
extension?UIView?{??func?insertSubview(view:?UIView,?at?index:?Int)func?insertSubview(view:?UIView,?aboveSubview?siblingSubview:?UIView)func?insertSubview(view:?UIView,?belowSubview?siblingSubview:?UIView)}我們調(diào)用時(shí)也是通過(guò)參數(shù)標(biāo)簽來(lái)區(qū)分不同的方法:
someView.insertSubview(view,?at:?3) someView.insertSubview(view,?aboveSubview:?otherView) someView.insertSubview(view,?belowSubview:?otherView)但是,當(dāng)我們創(chuàng)建一個(gè)函數(shù)的引用時(shí),就會(huì)產(chǎn)生一個(gè)歧義,即無(wú)法確定調(diào)用的是 UIView 的哪個(gè)方法
let?fn?=?someView.insertSubview?//?ambiguous:?could?be?any?of?the?three?methods我們可以使用類(lèi)型注解來(lái)消除歧義
let?fn:?(UIView,?Int)?=?someView.insertSubview????//?ok:?uses?insertSubview(_:at:)let?fn:?(UIView,?UIView)?=?someView.insertSubview?//?error:?still?ambiguous!但是上面的代碼后者因?yàn)?(UIView, UIView) 存在兩個(gè)方法(aboveSubview 和 belowSubview),所以還是存在歧義,只能用閉包的方式來(lái)指名傳遞的方法:
let?fn:?(UIView,?UIView)?=?{?view,?otherView?inbutton.insertSubview(view,?aboveSubview:?otherView) }這樣做法太乏味了,Swift 2.2 現(xiàn)在允許我們將函數(shù)命名為:函數(shù)名 + 參數(shù)標(biāo)簽的組合來(lái)消除歧義:
let?fn?=?someView.insertSubview(_:at:)let?fn1?=?someView.insertSubview(_:aboveSubview:)同樣的語(yǔ)法也可以用做初始化的引用:
let?buttonFactory?=?UIButton.init(type:)為指定的方法生成一個(gè) Objective-C 選擇器:
let?getter?=?Selector(NSDictionary.insertSubview(_:aboveSubview:))?//?produces?insertSubview:aboveSubview:.引用 Objective-C 的選擇器方法
SE-0022: Referencing the Objective-C selector of a method
在 Swift 2,Objective-C selectors 通常會(huì)根據(jù)其字面值寫(xiě)成字符串常量,比如 "insertSubview:aboveSubview:" 這樣比較容易出錯(cuò),例如下面的:
navigationItem.rightBarButtonItem?=?UIBarButtonItem(title:?"Tap!",?style:?.Plain,?target:?self,?action:?"buttonTaped")如果你眼神夠好,會(huì)發(fā)現(xiàn)我把 buttonTapped 寫(xiě)成了 buttonTaped,但 Xcode 也不會(huì)給我報(bào)錯(cuò)。這一切在 Swift 2.2 終于得到解決,字符串作為 selector 被 deprecated 了,今后該這么寫(xiě) #selector(buttonTapped),這樣發(fā)生拼寫(xiě)錯(cuò)誤,也能及時(shí)得到編譯器的提醒。
即使在純 Swift 環(huán)境中(完全與 Objective-C 完全無(wú)關(guān)),我們也可以通過(guò) #selector(Swift 方法名) 的方式來(lái)實(shí)現(xiàn) Swift 的 selector
control.sendAction(#selector(MyApplication.doSomething),?to:?target,?forEvent:?event)extension?MyApplication?{??@objc(jumpUpAndDown:)??func?doSomething(sender:?AnyObject?)?{?…?} }創(chuàng)建一個(gè) Selector 的引用
let?sel?=?#selector(UIView.insertSubview(_:at:))?//?produces?the?Selector?"insertSubview:atIndex:"編譯期 Swift 的版本檢查
SE-0020: Swift Language Version Build Configuration
在大部分時(shí)候,隨著 Swift 版本更新語(yǔ)法也會(huì)有較大調(diào)整,但是第三方類(lèi)庫(kù)的維護(hù)者們希望他們的庫(kù)能夠同時(shí)兼容不同版本的 Swift,目前可行的辦法是同時(shí)維護(hù)多個(gè)分支來(lái)支持不同版本的語(yǔ)言。
Swift 2.2 提供了新的選項(xiàng)使你將同一個(gè)版本的 Swift 代碼集中在同一個(gè)文件中,而編譯器會(huì)在編譯時(shí)選擇具體的 Swift 版本來(lái)執(zhí)行
#if?swift(>=2.2)print("Running?Swift?2.2?or?later")#elseprint("Running?Swift?2.1?or?earlier")#endif類(lèi)似于現(xiàn)存的 #if os() 構(gòu)建選項(xiàng),這個(gè)選項(xiàng)決定了編譯器隨后生成的代碼,如果你使用的是 Swift 2.2,那么第二個(gè) print() 將不會(huì)被看到。
其它一些特性
蘋(píng)果 Swift 官方 Blog 沒(méi)有提到的 Swift 2.2 一些新特性
++ 和 -- 將被取消
Swift 2.2 正式將 ++ 和 -- deprecates 掉了,意味著雖然在 Swift 2.2 版本還能工作,但編譯器會(huì)給你一個(gè)警告。但在 3.0 版本會(huì)被完全移除。
你可以使用 += 1 和 -= 1 來(lái)替代,至于為什么要將其移除,有這么幾個(gè)解釋:
寫(xiě) ++ 并不比 +=1 能節(jié)省多少時(shí)間
++ 對(duì)學(xué) Swift 沒(méi)有任何幫助,+= 至少可讀性更好
傳統(tǒng) C styel for 循環(huán)中的 -- 也被 deprecated 了
傳統(tǒng) C 風(fēng)格的 for 循環(huán)被干掉了
也就是說(shuō)下面這種寫(xiě)法在 2.2 的版本被 deprecates 了
for?var?i?=?1;?i?<=?10;?i?+=?1?{print("\(i)?SwiftGG?awesome") }以后要這么寫(xiě)了:
for?i?in?1...10?{????print("\(i)?SwiftGG?awesome") }如果想要?jiǎng)?chuàng)建一個(gè)由大到小的范圍,你按照下面的寫(xiě)法編譯或許沒(méi)問(wèn)題,但運(yùn)行時(shí)會(huì)崩潰
for?i?in?10...1?{????print("\(i)?SwiftGG?awesome") }應(yīng)當(dāng)這么寫(xiě):
for?i?in?(1...10).reverse()?{????print("\(i)?SwiftGG?awesome") }另一種選擇是使用標(biāo)準(zhǔn)的快速枚舉來(lái)遍歷數(shù)組
var?array?=?Array(1...10)for?number?in?array?{????print("\(number)?green?bottles") }數(shù)組和其他一些 slice types 現(xiàn)在有 removeFirst() 方法了
Swift 2.2 終于為我們帶來(lái)了 removeFirst() 方法,該方法將從數(shù)組中移除第一個(gè)元素,然后返回給我們,可以試驗(yàn)一下
var?array?=?Array(1...5)array.removeFirst()for?number?in?array?{????print("the?\(number)?bird") }使用 removeLast() 時(shí)要注意,如果是空數(shù)組,會(huì)崩潰,因此可以用 popLast() 來(lái)替代,該方法會(huì)處理空數(shù)組的情形(返回 nil)
元組 splat 語(yǔ)法被廢除了
們可以用下面的方式定義一個(gè)函數(shù),在 Swift 2.2 之前可以有兩種方式調(diào)用
func?foo(a?:?Int,?b?:?Int)?{}第一種我們經(jīng)常使用,為函數(shù)的每個(gè)參數(shù)都傳遞相對(duì)應(yīng)的值
foo(42,?b?:?17)或者我們可以利用一個(gè)大部分開(kāi)發(fā)者都不那么熟悉的特性(tuple splat)來(lái)調(diào)用
let?x?=?(1,?b:?2)foo(x)后者這種語(yǔ)法糖實(shí)在沒(méi)什么實(shí)際意義,在 Swift 2.2 被 deprecated,將在未來(lái)的版本移除。
var 參數(shù)被廢除了
var 參數(shù)提供的益處微乎其微,而且容易讓人與 inout 混淆,因此在 Swift 2.2 中被移除了。
舉個(gè)例子 sayHello() 函數(shù)使用了 var 參數(shù):
func?sayHello(var?name:?String,?repeat?repeatCount:?Int)?{name?=?name.uppercaseString????for?_?in?0?..<?repeatCount?{????????print(name)} }sayHello("numbbbbb",?repeat:?5)結(jié)果是 NUMBBBBB 將會(huì)被打印 5 遍,這是因?yàn)閰?shù) name 經(jīng) var 修飾后成為變量,然后執(zhí)行 uppercaseString 方法轉(zhuǎn)換為大寫(xiě),如果沒(méi)有 var 關(guān)鍵字,name 是常量,執(zhí)行 uppercaseString 會(huì)失敗。
var 和 inout 之間的差異非常微妙:
使用 var,讓你可以在函數(shù)內(nèi)部修改參數(shù)
使用 inout,甚至可以讓你的改變延續(xù)到函數(shù)結(jié)束后
我們可以在 Swift 2.2 中這么寫(xiě):
func?sayHello(name:?String,?repeat?repeatCount:?Int)?{????let?upperName?=?name.uppercaseString????for?_?in?0?..<?repeatCount?{????????print(upperName)} }sayHello("numbbbbb?",?repeat:?5)重命名 debug 標(biāo)識(shí)符:#line, #function, #file
在 Swift 2.1 和之前的版本,使用 __FILE__, __LINE__, __COLUMN__, 和 __FUNCTION__ 標(biāo)識(shí)符,在編譯時(shí)會(huì)被替換為文件名、行號(hào)、列號(hào)和函數(shù)名。
而在 Swift 2.2 這些舊的標(biāo)識(shí)符被更新為 #file, #line, #column 和 #function,如果你之前使用過(guò) Swift 2.0 的 #available 來(lái)檢查 iOS 版本,正如官方所說(shuō) # 意味這編譯器這里要執(zhí)行替換邏輯。
下面在 printGreeting() 函數(shù)中演示了新舊兩種 debug 標(biāo)識(shí)符:
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)轉(zhuǎn)載于:https://my.oschina.net/ShangGuanchen/blog/646419
總結(jié)
以上是生活随笔為你收集整理的swift2.2的新特性的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Head First C学习日志 第七章
- 下一篇: 一行命令让ElasticSearch支持