本文作者Andrew Wagner是一名資深的iOS和Web開發者,在Swift面世之后,他花費了許多的時間來研究Swift,在文章中,他不僅分享了Swift語言中讓iOS和OS X開發者頗為興奮的部分,還將其與Ruby、C++、Objective-C語言進行比較,總結出了Swift最為酷炫的七大功能。
1. 支持擴展結構和字面量
Swift中我最喜歡的一點就是可以擴展結構,這對于向現有結構中添加函數可謂大有裨益。對我來說,最好的例子就是它能夠實現向支持返還矩形類Rectangle中心點的CGRect添加一個中心法:
extension CGRect { var center : CGPoint { return CGPoint( x: self.origin.x + self.size.width / 2.0, y: self.origin.y + self.size.height / 2.0 ) } }
我不得不使用大量的Rectangle中心點,而如果我可以利用name來取代繁雜的數學計算,就能夠讓代碼的意圖顯得更加清晰明了。
此外,Swift還支持擴展字面量。在Ruby中,有一個名為repeat的非常有用的整數方法。基本上你可以使用塊來對一個整數調用repeat,即使重復定義整數也可被執行。然而在Swift中,一切就簡單得多:
extension Int { func repeat(block : () -> ()) { for i in 0..self { block() } } }接著,你就可以像這樣使用它:
3.repeat { // called 3 times println("hello") }注:如果唯一參數為閉包,則可省略括號。
2. 更加靈活的枚舉
Objective-C的枚舉語法著實欠佳,而Swift中的枚舉則更加靈活,你可以定義Swift的枚舉存儲任何類型的相關值,例如,網絡請求:
struct NetRequest { enum Method { case GET case POST(String) } var URL : String var method : Method } var getRequest = NetRequest(URL: "http://drewag.me", method: .GET) var postRequest = NetRequest(URL: "http://drewag.me", method: .POST("{"username": "drewag"}"))
GET請求并不具備請求主體,但POST卻具備。在方法枚舉中,如果沒有一個潛在閑置的成員變量,則可以直接定義POST內容主體。
關于這點,最好的例證就是Swift的Optional。Swift編譯器能夠給予我們許多語法上的甜頭,但落到實處,當你像這樣定義一個可選字符串時:var myString : String?,編譯器會將其轉換為var myString : Optional<String>。定義Optional:
enum Optional { case None case Some(T) }
3. 更為強大的泛型
集合在Objective-C中使用的都是最通用的泛型類,這就意味著開發者可以將任何值帶入一個包含混合對象的集合當中,但卻會造成集合類型模糊。這也是我所認為的C++比Objective-C更好的一個地方,C++有允許定義具備諸如vector<int>等特定類型的集合的Templates。Swift則借用了極為相似的語法,和可根據自我需求定義寫出靈活可重用的函數及類型的泛型(Generics)代碼。
接下來,讓我們來看一下不僅省時更能避免Bug出現的泛型的一個簡單示例:
class Word { enum PartOfSpeech { case Noun, Pronoun, Verb } var value : String var partOfSpeech : PartOfSpeech init(_ value: String, _ partOfSpeech : PartOfSpeech) { self.value = value self.partOfSpeech = partOfSpeech } } var sentence = [Word("I", .Pronoun), Word("ran", .Verb), Word("home", .Noun)] class Word { enum PartOfSpeech { case Noun, Pronoun, Verb } var value : String var partOfSpeech : PartOfSpeech init(_ value: String, _ partOfSpeech : PartOfSpeech) { self.value = value self.partOfSpeech = partOfSpeech } } var sentence = [Word("I", .Pronoun), Word("ran", .Verb), Word("home", .Noun)] sentence.append("quickly") // Cannot convert the expression's type '()' to type 'Word' sentence[0].lowercaseString // Could not find member 'lowercaseString' sentence[0].value.lowercaseString
4. 不同類型多重函數
在Swift中,當定義一個函數時,你可以定義一個或多個有名字和類型的值,作為函數的輸入,也可以定義某種類型的值作為函數執行結束的輸出。這對于定義可適用于各種類型但需要不同實現的函數來說絕對是如虎添翼。
func mathmaticallyAdd(a : Int, b : Int) -> Int { return a + b; } func mathmaticallyAdd(a : String, b : String) -> String { let aNum = a.bridgeToObjectiveC().intValue let bNum = b.bridgeToObjectiveC().intValue return "(aNum + bNum)" } mathmaticallyAdd(2, 7) // returns 9 mathmaticallyAdd("2", "7") // returns “9”
在上面的代碼段中,無緣無故地定義了mathmaticallyAddInts和mathmaticallyAddStrings兩個不同的函數,因為其用意很清楚,只需mathmaticallyAdd即可,那這樣就會顯得過于繁瑣冗長。定義使用Int,函數可以只使用+操作符,然而,在字符串實現中,該函數必須首先將字符串轉換為整數。
5. 屬性監視器
在Objective-C中,很多情況下我都會重寫屬性setter,以便我能在執行某個操作前后對值進行設置。如果我不想將屬性設置為atomic的話,那么我就需要引用代碼來充當實際值分配到我想要執行的那個操作上,這樣實在是太笨拙不堪了。
反觀Swift,它擁有一種內置的機制,能夠在移除對樣板代碼的需求任務完成前后進行監控并響應,這項功能稱之為屬性監視器(Property Observers)。你可以為屬性添加一個willSet或(和)一個didSet監視器,willSet在設置新的值之前調用,而didSet則是在新的值被設置之后立即調用。
class MyView : UIView { var aSubview : UIView { didSet { oldValue.removeFromSuperview() self.addSubview(aSubview) } } init(frame: CGRect) { self.aSubview = UIView() super.init(frame: frame) } }
willSet監視器會將新的屬性值作為固定參數傳入,在willSet的實現代碼中可以為這個參數指定一個名稱,如果不指定則參數仍然可用,而其默認名稱用newValue表示。同樣,didSet監視器也會將舊的屬性值作為參數傳入,可以為該參數命名或直接使用默認參數名oldValue。
6. 優雅的閉包占用列表
盡管Swift的內存管理仍然采用自動引用計數,但其對于語法卻有著極大的改善。在Swift中,無需在塊以外聲明weak或unsafe_unretained變量,你可以定義一個閉包應該如何使用捕獲列表來捕獲常量或變量。
class FilePickerController : UIViewController { var onDidPickFileWithPath : ((path : String) -> ())? } class DocumentViewController : UIViewController { var filePicker : FilePickerController? var content : String? init(fileAtURL URL: NSURL) { super.init(nibName: nil, bundle: nil) var request = NSURLRequest(URL: URL) // weak: Have self automatically set to nil if it is deallocated NSURLConnection.sendAsynchronousRequest(request, queue: nil) { [weak self] (response, data, error) in if let actualSelf = self { // Captures an owning reference "actualSelf" so interacting with it in this // block is safe actualSelf.content = NSString(data: data, encoding: NSUTF8StringEncoding) } } } func presentFilePicker() { if !filePicker { filePicker = FilePickerController(); // unowned: Don't worry about the nil case if you know it will never happen filePicker!.onDidPickFileWithPath = { [unowned self] (path : String) in self.content = NSString.stringWithContentsOfFile(path) as? String } } self.presentViewController(filePicker!, animated: true, completion: nil) } }Swift提供了一種頗為優雅的方法來解決循環強引用所存在的問題,稱之為閉包占用列表(Closuer Capture List)。在上段代碼中,除了可以初始化URL的DocumentViewController之外,我還定義了一個FilePickerController類。當用戶選擇一個文件時,它便會提供一個閉包成員,而DocumentViewController則有一個方法來呈現該文件并處理用戶選擇文件的操作。此時,如果該閉包強烈捕獲self,則將產生循環強引用。
這種情況下,我明白只要有DocumentViewController,閉包也就會永遠存在,所以我可以通過[unowned self]使用無主引用來解決循環強引用,這比Objective-C可要干凈得多了。
7. 構造過程更安全
在使用Objective-C編寫程序時,我常常會犯一個錯誤,總是要我抓狂地花上好幾分鐘去調試它。這個問題非常普遍,就是在我想加載內容進去的一個類中,有一個可變數組,為了能夠被添加進去,首先需要初始化數組,但我卻老是會忘記。
而在Swift中,編輯器將會處于兩方面的原因截獲它,其一便是你必須對取值為空的屬性進行處理,在定義一個構造器之后,如果你留下任何未初始化的成員變量,編譯器便會顯示錯誤,這將節省我大量的時間,如下:
class FileSystemItem { var path : String init(path : String) { self.path = path } } class Directory : FileSystemItem { var contents : FileSystemItem[] init(path : String) { self.contents = [] // required super.init(path: path) // required } func loadContents() { // ... // self.contents.append(file) // ... } }
在Objective-C中有一個名為指定構造器(Designated Initializers)的概念,每個類都應該有一個將全部類都初始化在安全狀態的構造器,而其他構造器則應該調用一個指定構造器來確保類良好,但很遺憾這在Objective-C中并不是強制執行的。而Swift則將其融入編譯器,這就意味著Directory類必須調用init(path)構造器,否則將顯示錯誤,而在path沒有被定義的情況下,該類將無法實現,當類想要獲得更大的繼承時,便顯得尤為重要。
現在,您還可以進入Swift的mobilehub主頁進行資源分享和討論。
本文為CSDN編譯整理,未經允許不得轉載,如需轉載請聯系market#csdn.net(#換成@)