Swift的一些特點,重要概念和應用
CSDN移動將持續為您優選移動開發的精華內容,共同探討移動開發的技術熱點話題,涵蓋移動應用、開發工具、移動遊戲及引擎、智慧硬體、物聯網等方方面面。如果您想投稿、參與內容翻譯工作,或尋求近匠報道,請傳送郵件至tangxy#csdn.net(請把#改成@)。
簡介
Swift語言從WWDC2014釋出開始,到現在已經發展了一年多時間,越來越多的開發者也開始學習和使用這門語言。但就我所瞭解的情況來看,在實際專案中Swift的應用還是比較少。開發者給它的評價也是褒貶不一,有的說它的安全性高,有的說它的特性多,有的說它的學習成本高,還有的說它是一個玩具語言不適合工程。其實這都很正常,因為一千個人眼中有一千個哈姆雷特,語言的喜好本身就是一件很主觀的事情。具體這個語言怎麼樣,適不適合工程,需要每個人實踐之後才能得出自己結論。
Swift的特點
支援Unicode
程式碼原生支援Unicode字元。不僅在字串中,甚至變數名、函式名等都能直接使用Unicode字元。雖然看上去很強大,但似乎並沒有什麼用,應該沒人喜歡在程式設計時不停的切換輸入法吧?
安全的型別
採用嚴格的型別,並去掉了隱式型別轉換:
隱式型別轉換一直是一把雙刃劍,雖然使用便利,但是可能引入一些很難除錯的BUG,不容忽視。把隱式型別轉換摘除,利大於弊。
從型別層面將空值nil隔離,使用時要求對空值進行處理:
嚴格的語義邏輯
Swift對C語系一些常見的語義邏輯漏洞進行了修改,比如if等條件限定為Bool型別,賦值“=”操作不再有返回值等(其實有,是Void,即空元組“()”)。雖然使用上沒有之前那麼方便和靈活,但這種改變能杜絕很大一部分的手誤BUG,比如“==”寫成“=”,還能避免一些偷懶所引入的很隱祕的坑,對程式的穩定性和程式設計師好習慣的培養有很大幫助。
易用性
沿用並完善了Objective-C的函式中綴呼叫方式,引數有了真正的名字,呼叫時帶上引數名能讓函式介面更容易理解,可讀性更好:
優化了可變引數定義和使用方式:
在C語系中定義可變引數還需要va_list、va_start等,一段時間不用根本想不起來怎麼寫,還得上網查,而在Swift中只需要遍歷一個數組就能取到所有引數,非常方便。
優化了控制流的使用:
Switch的case可以連寫,而且加入了很多實用的匹配模式,比如匹配範圍、元組、條件等,還可以自定義匹配模式,十分強大。另外分支預設是break方式,不像在C語言中,明知道90%的case都是要break的,還要強制寫上。還有一個很好的優化就是加入了跳轉標籤,在多重迴圈間控制轉移的時候更靈活了。
加入了很多實用語法糖,僅僅一個閉包就有這麼多簡寫方式:
這些語法糖能節省大量的開發時間和程式碼量,使用得當也能讓程式碼更清晰,可讀性更好。當然如果濫用的話可能起反作用。
豐富的語言特性
Swift支援類、協議、繼承、多型等面向物件的語言特性:
也有高階函式、閉包等函數語言程式設計特性:
還有泛型別、泛函數、泛協議等泛型程式設計特性以及操作符自定義等新特性:
總之Swift加入了大量的流行語言特性,功能靈活、強大,但是語法點也增加了很多,導致語法學習難度增大,各位可以按需要進行有針對性的學習。
Swift的一些重要概念
值與引用
值與引用型別在某些情況下與我們的程式設計習慣可能會有些衝突,是Swift初學常遇到的一個坑。先看一個例子,定義兩個陣列arr1和arr2,arr2用arr1賦值:
修改arr2[0]後,發現arr1[0]並沒有修改。由此可知在Swift中,Array型別是值型別。再來看看Array的實現方式。按住Command鍵點選Array型別,進入到Swift庫,可以看到如下定義:
Array和Dictionary,包括Int、Set、Double等基本內建資料型別都是由struct及其實現的一組協議構成。由於struct型別是值型別,所以Swift中的基本內建型別都是值型別。由於是值型別,所以每次賦值或者傳參的時候都會有個拷貝的過程。我們先來做個實驗:
執行以上程式碼可以發現,在修改了arr[0]的情況下,賦值的時間是1783ms(模擬器下),而僅是讀取的情況,賦值時間是0ms。這個結果說明值傳遞使用了寫時拷貝(copy on write)技術,也就是說只要不修改儲存的資料,副本和原值共享記憶體區域。因此我們在使用這些值型別的時候一般有一些原則:
- 儘量限制資料規模;
- 如果資料規模較大,儘量不用作賦值和傳參;
- 如果需要賦值和傳參,儘量定義為常量或後續不修改資料;
- 如果以上都不能避免,可以使用引用型別代替值型別,如 NSMutableArray等;
Optional
Optional可選型別是Swift的一個重要概念,也是初學者比較難理解的地方。我們經常能在閱讀程式碼時看到下面這樣的程式碼:
有問號有感嘆號,放的位置不一樣作用也不一樣,用的時候搞不清什麼時候該用哪個,只好跟著xcode提示走,掉進坑裡也不知道。要搞清楚這個問題先要明白Optional是什麼。在Swift庫中可以找到Optional的定義:
Optional其實是個泛型列舉,定義時的型別比如 Int? 等價於 Optional<Int>。它實現了NilLiteralConvertible協議,所以能被nil字面量賦值。當可選型別用nil賦值時,nil被轉化成None,當用非nil賦值時,轉化為Some(T)。Some(T)這種形式叫做相關值(Associated Values),可以這麼理解,這是一種名叫Some,能存放T型別的盒子,我給它賦的值就放在這個盒子裡儲存。
可選型別相當於對nil或實際的值做了封裝,所以使用Optional時需要將實際的值從Some(T)中取出,這個過程就叫解可選(unwrap)。
先來看看問號定義的可選型別:
問號定義的可選型別就是普通的Optional,常用的解可選方式有三種,如上圖所示。
- 先對opt進行判空,若不為空則用 opt! 進行強制解可選。“!”在這裡的意思就是,我確定該值不為空,請直接取出實際值;
- 用if let unwrap = opt 來解可選。這是Swift為解可選做的語法糖,叫做可選繫結。如果opt為nil則進入else分支,若不為nil則將實際值提取到unwrap,然後進入if分支;
- 利用opt ?? 0解可選。“??”叫空合運算子(Nil Coalescing Operator),作用就是,如果opt為nil,表示式值取後面的預設值,否則表示式返回opt的實際值;
再來看看感嘆號定義的可選型別:
感嘆號定義的可選型別叫做隱式解可選型別。在概念上與問號定義的可選型別並沒有什麼區別,只是在使用形式上有些不同。
隱式解可選型別在用作右值時並不需要人為進行解可選,它預設由編譯器進行強制解可選,相當於編譯器自動在後面新增一個感嘆號。這樣在使用的時候就不用強制對空值進行處理,更方便一點,但是卻十分危險,因為必須由人工保證opt_f在用到的地方必須不為nil,否則程式會crash。而人腦總不如機器保險,因此一般情況不建議使用隱式可選型別。
問號還有一種使用場景叫做可選鏈。先來看看這麼一種情況:
我定義了一個Person類,類有father屬性表示他的父親,而孤兒可能不知道父親是誰,因此father是個可選型別。現在來了一個需求,需要獲取某個人曾祖父的名字,我們可能會寫出如下程式碼:
看上去很不美觀,寫起來更費勁。針對這種情景,Swift做了優化,加入了可選鏈這種語法糖,上面的程式碼就能改寫為:
用問號把方法呼叫或屬性獲取等連線起來,組成一條呼叫鏈。意思就是我不關心中間過程,只關心最後結果,如果中途任何環節返回了nil,就直接返回nil,否則返回最後結果。這樣就能節省很多工作量,讓程式碼看上去更美觀,結構更清晰。需要注意的一點是,雖然name是確定的String型別,但是由於可選鏈上任一環節都可能返回nil,最後得到的是一個String?可選型別,所以還需要做一次解可選操作。
Swift的應用
與Cocoa互動
要用Swift寫App首先需要了解的就是UI怎麼寫、系統功能怎麼呼叫。由於Swift並沒有重寫系統功能庫,只是對Cocoa進行了橋接,所以要呼叫系統功能就要與Cocoa的互動。Swift與Cocoa互動的細節非常多,但是並沒有太大難度,因為Cocoa的使用與用Objective-C開發時沒有太大不同,一般來說跟著Xcode的提示走基本都沒有問題。需要注意的幾點:
- Swift沒有發訊息這種方式,所有方法呼叫和屬性訪問都是用點操作, alloc、init橋接成了swift中的構造器,使用時直接用class例項化的方式即可。
- 一些常用型別在swift中都有對應,可以相互轉換,通常情況建議使用swift的型別。
- Object-C中有些機制比如 KVO等在swift中不支援,如果要使用,需要帶上@objc、dynamic等標記。
多執行緒
Swift語言本身並沒有多執行緒支援,因此要使用多執行緒還是要呼叫NSThread、GCD等:
此外,Object-C中一個很方便的程式碼互斥方式@synchronized在swift中不支援,因此要實現程式碼互斥需要自己使用鎖。當然也可以模仿@synchronized自定義一個synchronized方法,利用尾部閉包造出原來的感覺:
單例
單例模式是移動開發中常見的一種模式,其一般要求是:
- 只初始化一次;
- 執行緒安全;
在Object-C中單例通常是用dispatch_once來實現的,在Swift中同樣可以這麼做。但是由於Swift中支援全域性變數、常量的lazy初始化,我們可以簡化單例的實現:
通過let定義單例例項常量來保證執行緒安全,通過全域性常量的lazy初始化來保證單例只初始化一次。
ARC
Swift中的ARC原理與Object-C中一樣,只不過在使用形式上有所不同。Swift中所有引用預設都是強引用,另外加入了兩個ARC標記:
- weak:類似Object-C的weak,弱引用,引用失效後引用自動賦值為nil;
- unowned:類似Object-C中的unsafe_unretain,弱引用,引用失效後保留引用本身;
由於weak引用在使用過程中可能變為nil,所以weak引用必須是可選型別(Optional)。另外Swift對閉包捕獲的變數的標記做了優化:
在Object-C中這種情況需要在閉包之外定義一個weak self,然後閉包捕獲weak self。Swift中只需要把weak self寫在閉包引數表之前的中括號中即可完成同樣功能,這種方式程式碼結構更清晰,可讀性更好。需要注意的是在閉包內self是weak的,是個可選型別,因此使用self時需要解可選。
Delegate
代理模式是開發中很常見的一種模式,它的實現原理與Object-C類似,也是通過協議來確定代理介面,用實現協議的例項來充當代理:
不同的地方在於,Object-C中delegate是id型別的,而swift中協議是一種基本型別,delegate可以直接定義為協議型別,這樣能讓代理的職責更明確。要注意的地方是delegate是weak的,所以必須使用可選型別,而且weak標記的必須是引用,所以protocol必須打上class標記,表示該協議只能被class實現,這樣協議型別才能當做引用型別使用。
自定義操作符
Swift中可以自定義操作符,自定義一個操作符需要兩個要素:
- 操作符特性描述,包括:操作符位置 prefix、infix、postfix等,操作符的結合性left、right以及操作符的優先順序;
- 操作符功能函式,引數和返回值需要與操作符特性一致,比如 prefix的操作符接受一個引數,返回一個值;
比如我們可以定義一個笑臉操作符,他在字串後面新增一個笑臉(^_^):
函數語言程式設計
在Swift中,函式是first-class,也就是說函式可以作為引數傳入也可以作為返回值返回,也可以給變數賦值。函數語言程式設計是一種很強大、很靈活的思想,在Object-C中也有block等函數語言程式設計思想,但並不明顯,在Swift中這一點得到了強化。
比如說可以實現一個函式工廠:
泛型程式設計
Swift中增加了泛型這個概念,泛型是一種很好的程式碼複用機制,用於將型別不同但實現相同的程式碼統一起來。泛型在Swift中應用非常廣泛,你只要跳轉到Swift的庫檔案就會發現型別、操作符、全域性函式的定義中大量應用了泛型,因此不再做詳細介紹。這裡有一個小例子,利用操作符自定義、函式式和泛型在程式碼中實現管道機制:
總結
Swift有著更嚴格的型別和更規範的語義,針對容易造成BUG的點進行了優化,又加入了很多不錯的語法糖,程式碼更簡潔、更安全。它吸收了大量其他語言的優秀特性,可以實現一些強大靈活的設計模式,但也造成了其語法繁雜的缺點,增加了學習的難度。Swift本身的設計目標是高效、靈巧,但由於需要相容Cocoa,導致其設計受到牽制,引入了一些有隱患的設計。由於這些特點,編寫Swift程式碼其實很依賴IDE的輔助,但目前為止xcode的表現還不穩定,經常提示錯誤,再加上幾乎每次xcode更新都伴隨著Swift語法修改,Swift想要廣泛應用於實際工程還有一段路要走,而Swift2.0的釋出和Swift開源化,無疑將加快這個程序。