大綱如下:
- Fundamentals
- Pattern Matching
- Availability Checking
- Protocol Extensions
- Error Handling
Fundamentals
枚舉的反射
Swift1.2 :
enum Animals{
case Dog, Cat, Troll, Dragon
}
let a = Animals.Dragon
print(a) //打印 (Enum Value)
Swift2. 0:
enum Animals{
case Dog, Cat, Troll, Dragon
}
let a = Animals.Dragon
print(a) //打印 (Animals.Dragon)
調(diào)試過程中更直觀地顯示數(shù)據(jù)慧耍。
枚舉中的關(guān)聯(lián)值
Either:兩者選其一未辆,非此即彼的意思拉背。
這里構(gòu)造一個(gè) Either 枚舉断箫,且綁定 T1 和 T2 兩個(gè)泛型。Either枚舉有兩種情況:要么就是First澳淑,關(guān)聯(lián)值為 T1 類型比原;要么就是 Second ,關(guān)聯(lián)值為 T2 類型杠巡。想法很nice量窘,但是現(xiàn)實(shí)很殘酷,swift1.2中無法編譯通過忽孽,往往解決方法是自己構(gòu)造一個(gè)Box類绑改。
swift1.2 :
enum Either<T1,T2>{
case First(T1)
case Second(T2)
}
// swift1.2中直接報(bào)錯(cuò)
swift2.0 :
enum Either<T1,T2>{
case First(T1)
case Second(T2)
}
// 完美執(zhí)行
枚舉中的遞歸
swift1.2 中不允許采用遞歸,所以使用以下方式編譯報(bào)錯(cuò):
swift1.2 :
enum Tree<T>{
case Leaf(T)
case Node(Tree,Tree)
}
// 編譯器報(bào)錯(cuò)兄一,通過解決方式是自定義一個(gè)Box類
swift2.0 :
enum Tree<T>{
case Leaf(T)
indirect case Node(Tree,Tree)
//在使用遞歸枚舉用例的地方使用修飾符 indirect
}
repeat-while 替換 do-whi2
可能是因?yàn)橐肓?do-catch 語句厘线,為了避免混淆,所以將其改成了 repeat-while
聲明出革,感覺有一定意義吧造壮。
Option Sets
Swift1 中討論最多的就是使用位掩碼(bit mask)來實(shí)現(xiàn),學(xué)過C語言的朋友應(yīng)該再熟悉不過骂束。
swift1 :
// 設(shè)置動(dòng)畫選項(xiàng)時(shí)如此聲明耳璧,表示該動(dòng)畫具有 重復(fù)、漸進(jìn)漸出以及卷曲動(dòng)畫 三個(gè)屬性
viewAnimationOptions = .Repeat | .CurveEaseIn | .TransitionCurlUp
但是往往會(huì)造成誤解:
viewAnimationOptions = .Repeat | .CurveEaseIn | .TransitionCurlUp
viewAnimationOptions = nil
if viewAnimationOptions & .TransitionCurlUp != nil {}
采用與(&)展箱、或(|)操作來進(jìn)行結(jié)合旨枯,美名其曰是對集合進(jìn)行操作,實(shí)際不過是C語言的位操作罷了混驰。甚至類型上也顯得很牽強(qiáng)攀隔,要知道 options 是一個(gè)位掩碼值,做一個(gè)與(&)操作后竟然可以等于nil(恰當(dāng)?shù)靥幚響?yīng)該為一個(gè)空集合栖榨,不是嗎昆汹?)。
swift2.0 :
// 采用中括號(hào)[]來包括婴栽,讓其看起來是對集合進(jìn)行操作满粗,倘若沒有元素,即[]空集合 而非無值nil!
viewAnimationOptions = [.Repeat, .CurveEaseIn, .TransitionCurlUp]
viewAnimationOptions = []
// contain操作就是簡單.TransitionCurlUp 是否包含于集合中 很形象愚争!
if viewAnimationOptions.contains(.TransitionCurlUp) {
自定義一個(gè)可選集合:
struct MyFontStyle : OptionSetType {
// 一定要有rawValue
let rawValue : Int
static let Bold = MyFontStyle(rawValue: 1)
static let Italic = MyFontStyle(rawValue: 2)
static let Underline = MyFontStyle(rawValue: 4)
static let Strikethrough = MyFontStyle(rawValue: 8)
}
// style 是MyFontStyle類型 本例構(gòu)造了一個(gè)集合
myFont.style = []
myFont.style = [.Underline]
myFont.style = [.Bold, .Italic]
if myFont.style.contains(.StrikeThrough) {}
更多請見我寫的這篇文章[Swift2.0系列]OptionSetType使用
函數(shù)和方法
swift1.2 :
// 這里的save是全局函數(shù)(function)
func save(name: String, encrypt: Bool) { ... }
// 類中的save是方法(method)
class Widget {
func save(name: String, encrypt: Bool) { ... }
// 調(diào)用全局函數(shù) 和 類方法
save("thing", false)
widget.save("thing", encrypt: false)
注意調(diào)用函數(shù)和方法的標(biāo)簽映皆,函數(shù)是沒有任何外部標(biāo)簽的挤聘;而方法除了第一個(gè)沒有外部標(biāo)簽,從第二個(gè)開始都帶標(biāo)簽劫扒。
swift2.0為了和oc統(tǒng)一檬洞,現(xiàn)在函數(shù)也是從第二個(gè)參數(shù)開始默認(rèn)給定外部標(biāo)簽。
swift2.0 :
func save(name: String, encrypt: Bool) { ... }
class Widget {
func save(name: String, encrypt: Bool) { ... }
// 注意這里 函數(shù)也是帶標(biāo)簽了
save("thing", encrypt: false)
widget.save("thing", encrypt: false)
你可能好奇為什么第一個(gè)參數(shù)沒有外部標(biāo)簽的沟饥,因?yàn)榫幾g器自動(dòng)用下劃線_
替換掉了第一個(gè)參數(shù)的外部標(biāo)簽,表示省略外部標(biāo)簽湾戳,在調(diào)用時(shí)是不會(huì)顯示出來的贤旷,就像這樣:
// 注意_ 和 name之間是有空格的。 _表示忽略外部標(biāo)簽砾脑, name表示函數(shù)內(nèi)部使用參數(shù)標(biāo)簽
// 而encrypt 默認(rèn)外部標(biāo)簽和內(nèi)部標(biāo)簽是為同一個(gè)幼驶,即encrypt
func save(_ name: String, encrypt: Bool)
// 還可以這么寫
func save(_ name: String,encrypt encrypt: Bool)
// 顯然寫兩個(gè)encrypt 毫無意義,除非你想自定義外部參數(shù)標(biāo)簽名稱
// 想要第一個(gè)參數(shù)也具有外部參數(shù)標(biāo)簽名韧衣,可以這么干
func save(externalName name: String,encrypt encrypt: Bool)
關(guān)于Diagnostics
swift2.0中對警告和錯(cuò)誤更為敏感和友好盅藻,不像swift1.2中模糊不清的錯(cuò)誤信息,而是“一針見血”畅铭,方便你“對癥下藥”氏淑!當(dāng)然swift2.0還新增了一些其他有用的經(jīng)過,譬如變量聲明卻沒有使用硕噩,或者變量僅作為常量即可假残,無須使用var
關(guān)鍵字聲明為變量等等
Pattern Matching
**swift1.0 **很容易出現(xiàn)金字塔“鞭尸”的情況:
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let dest = segue.destinationViewController as? BlogViewController
if let blogIndex = tableView.indexPathForSelectedRow()?.row {
if segue.identifier == blogSegueIdentifier {
dest.blogName = swiftBlogs[blogIndex]
...
...
}
}
}
}
swift1.2 中我們可以采用條件符合的寫法,但是也不盡如意:
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// 注意我們把三個(gè)條件整合成了一個(gè)if語句 但是還是逃脫不了被鞭尸
if let dest = segue.destinationViewController as? BlogViewController
let blogIndex = tableView.indexPathForSelectedRow()?.row
where segue.identifier == blogSegueIdentifier {
dest.blogName = swiftBlogs[blogIndex]
...
...
}
}
再舉個(gè)Json解析的例子:
// 返回Either枚舉值 要么就是有值為.First(Person) 要么沒有值.Second(String)
func process(json: AnyObject) -> Either<Person,String> {
// 還有一點(diǎn)值得注意 我們聲明的name 和 year 都是可選類型值 所以都是需要解包的
let name: String? = json["name"] as? String
if name == nil {
return .Second("missing name")
}
let year: Int? = json["year"] as? Int
if year == nil {
return .Second("missing year")
}
let person = processPerson(name!, year!)// 解包
return .First(person)
顯然解包行為的代碼令人無法直視炉擅,所以嘍你可能會(huì)在聲明name 和 year就直接采用隱式可選類型辉懒,即使用String!
和Int!
來干,只能說治標(biāo)不治本吧谍失。ps:記住顯示可選類型和隱式可選類型都是可選類型眶俩,意味著它們都有值不存在的情況,即nil
快鱼;區(qū)別在于顯示可選類型要解包颠印,隱式可選類型的解包由編譯器幫你完成,換句話說你不用每次都使用!
來解包拉攒巍,但是這樣造成的嚴(yán)重后果是倘若值為nil了嗽仪,那么程序就crash嘍,所以且碼且謹(jǐn)慎柒莉。
swift2.0 引入了guard
聲明闻坚,意為警衛(wèi),充當(dāng)一個(gè)保護(hù)者兢孝,只有條件滿足時(shí)才可以執(zhí)行下面的程序窿凤;倘若不滿足仅偎,就必須提前突出嘍!你可以使用return break 等關(guān)鍵字雳殊,當(dāng)然也可以使用no-return 方式橘沥,譬如assert exception等等。
// 現(xiàn)在是不是簡潔很多了 值得一提的是 name year 的作用域是整個(gè)函數(shù)
// 而if-let 中的解包數(shù)據(jù)作用域只能是if作用域
func process(json: AnyObject) -> Either<Person,String> {
guard let name = json["name"] as? String,
let year = json["year"] as? Int else
return .Second(“bad input”)
}
let person = processPerson(name, year)
return .First(person)
}
再來談?wù)?switch 語句夯秃,我很喜歡swift中的switch用法座咆,可以使用符合條件操作等,但是有一點(diǎn)我也很討厭仓洼,當(dāng)我想要匹配的條件僅有一個(gè)的時(shí)候介陶,竟然還要寫switch(){//一堆東西}
,就像下面這樣:
// 根據(jù) bar() 方法返回值進(jìn)行switch匹配 執(zhí)行對應(yīng)操作
switch bar() {
case .MyEnumCase(let value):where value != 42:
doThing(value)
default: break
}
swift2.0 新增匹配方式:
if case .MyEnumCase(let value) = bar() where value != 42 {
doThing(value)
}
for-in中的匹配
swift1.2 前你是否遇到過這種情況:
// for-in 每一次循環(huán)都要執(zhí)行 value != ""的判斷 是否令你不爽
for value in mySequence {
if value != "" {
doThing(value)
}
}
你有考慮過這么干嗎:
for value in mySequence where value != "" {
doThing(value)
}
假設(shè) mySequence 是一個(gè)枚舉序列呢色建?哺呜?? 難道你要在for-in中每一個(gè)循環(huán)執(zhí)行一次switch
匹配嗎箕戳? Swift2.0中對這些say goodbye 吧某残!
for case .MyEnumCase(let value) in enumValues {
doThing(value)
}
Availability Checking
請見這篇文章:
[Swift2.0系列]API可用性檢查(譯)
Protocol Extensions
給已實(shí)現(xiàn)的類型進(jìn)行Extension 想必很多開發(fā)者都嘗試過,譬如為Array擴(kuò)展一個(gè)countIf
方法:
extension Array{
func countIf(match:Element -> Bool) -> Int{
var n = 0
for value in self{
if match(value){ n++}
}
return n
}
}
這種做法很常見陵吸,但你要知道并不是只有Array適用countIf方法的玻墅,其他序列同樣適用!所以嘍我們會(huì)聲明一個(gè)全局函數(shù)專門對傳入的序列進(jìn)行處理:
func countIf<T:CollectionType>(collection:T,
match:T.Generator.Element -> Bool) -> Int{
var n = 0
// WWDC pdf 為 for value in self 不知是否錯(cuò)誤 我改為 collection
for value in collection{
if match(value){ n++}
}
return n
}
但是通過全局函數(shù)來處理指定隊(duì)列可能會(huì)讓初學(xué)者很迷茫走越,他怎么知道有filter map這些函數(shù)呢椭豫?事實(shí)上swift1.0就是那么干的! 當(dāng)時(shí)用得相當(dāng)不爽旨指。
// 這種方式糟透了赏酥!
let x = filter(map(numbers) { $0 * 3 }) { $0 >= 0 }
swift2.0 :
let x = numbers.map { $0 * 3 }.filter { $0 >= 0 }
我們所要做的是對CollectionType協(xié)議進(jìn)行Extension,即為該協(xié)議實(shí)現(xiàn)默認(rèn)方法:
extension CollectionType {
func countIf(match: Element -> Bool) -> Int {
var n = 0
for value in self {
if match(value) { n++ }
}
return n
}
}
當(dāng)然你可以在Array中覆蓋次默認(rèn)實(shí)現(xiàn)谆构,而采用自定義實(shí)現(xiàn)裸扶。
請見這篇文章:
[Swift2.0系列]Protocol Extensions 基礎(chǔ)用法和實(shí)戰(zhàn)(初稿)
Error Handling
請見我寫的這篇文章: