本文作者Andrew Wagner是一名資深的iOS和Web開(kāi)發(fā)者,在Swift面世之后,他花費(fèi)了許多的時(shí)間來(lái)研究Swift,在文章中,他不僅分享了Swift語(yǔ)言中讓iOS和OS X開(kāi)發(fā)者頗為興奮的部分,還將其與Ruby、C++、Objective-C語(yǔ)言進(jìn)行比較,總結(jié)出了Swift最為酷炫的七大功能。
1. 支持?jǐn)U展結(jié)構(gòu)和字面量
Swift中我最喜歡的一點(diǎn)就是可以擴(kuò)展結(jié)構(gòu),這對(duì)于向現(xiàn)有結(jié)構(gòu)中添加函數(shù)可謂大有裨益。對(duì)我來(lái)說(shuō),最好的例子就是它能夠?qū)崿F(xiàn)向支持返還矩形類Rectangle中心點(diǎn)的CGRect添加一個(gè)中心法:
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中心點(diǎn),而如果我可以利用name來(lái)取代繁雜的數(shù)學(xué)計(jì)算,就能夠讓代碼的意圖顯得更加清晰明了。
此外,Swift還支持?jǐn)U展字面量。在Ruby中,有一個(gè)名為repeat的非常有用的整數(shù)方法?;旧夏憧梢允褂脡K來(lái)對(duì)一個(gè)整數(shù)調(diào)用repeat,即使重復(fù)定義整數(shù)也可被執(zhí)行。然而在Swift中,一切就簡(jiǎn)單得多:
extension Int { func repeat(block : () -> ()) { for i in 0..self { block() } } }接著,你就可以像這樣使用它:
3.repeat { // called 3 times println("hello") }注:如果唯一參數(shù)為閉包,則可省略括號(hào)。
2. 更加靈活的枚舉
Objective-C的枚舉語(yǔ)法著實(shí)欠佳,而Swift中的枚舉則更加靈活,你可以定義Swift的枚舉存儲(chǔ)任何類型的相關(guān)值,例如,網(wǎng)絡(luò)請(qǐng)求:
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請(qǐng)求并不具備請(qǐng)求主體,但POST卻具備。在方法枚舉中,如果沒(méi)有一個(gè)潛在閑置的成員變量,則可以直接定義POST內(nèi)容主體。
關(guān)于這點(diǎn),最好的例證就是Swift的Optional。Swift編譯器能夠給予我們?cè)S多語(yǔ)法上的甜頭,但落到實(shí)處,當(dāng)你像這樣定義一個(gè)可選字符串時(shí):var myString : String?,編譯器會(huì)將其轉(zhuǎn)換為var myString : Optional<String>。定義Optional:
enum Optional { case None case Some(T) }
3. 更為強(qiáng)大的泛型
集合在Objective-C中使用的都是最通用的泛型類,這就意味著開(kāi)發(fā)者可以將任何值帶入一個(gè)包含混合對(duì)象的集合當(dāng)中,但卻會(huì)造成集合類型模糊。這也是我所認(rèn)為的C++比Objective-C更好的一個(gè)地方,C++有允許定義具備諸如vector<int>等特定類型的集合的Templates。Swift則借用了極為相似的語(yǔ)法,和可根據(jù)自我需求定義寫(xiě)出靈活可重用的函數(shù)及類型的泛型(Generics)代碼。
接下來(lái),讓我們來(lái)看一下不僅省時(shí)更能避免Bug出現(xiàn)的泛型的一個(gè)簡(jiǎn)單示例:
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. 不同類型多重函數(shù)
在Swift中,當(dāng)定義一個(gè)函數(shù)時(shí),你可以定義一個(gè)或多個(gè)有名字和類型的值,作為函數(shù)的輸入,也可以定義某種類型的值作為函數(shù)執(zhí)行結(jié)束的輸出。這對(duì)于定義可適用于各種類型但需要不同實(shí)現(xiàn)的函數(shù)來(lái)說(shuō)絕對(duì)是如虎添翼。
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”
在上面的代碼段中,無(wú)緣無(wú)故地定義了mathmaticallyAddInts和mathmaticallyAddStrings兩個(gè)不同的函數(shù),因?yàn)槠溆靡夂芮宄?,只需mathmaticallyAdd即可,那這樣就會(huì)顯得過(guò)于繁瑣冗長(zhǎng)。定義使用Int,函數(shù)可以只使用+操作符,然而,在字符串實(shí)現(xiàn)中,該函數(shù)必須首先將字符串轉(zhuǎn)換為整數(shù)。
5. 屬性監(jiān)視器
在Objective-C中,很多情況下我都會(huì)重寫(xiě)屬性setter,以便我能在執(zhí)行某個(gè)操作前后對(duì)值進(jìn)行設(shè)置。如果我不想將屬性設(shè)置為atomic的話,那么我就需要引用代碼來(lái)充當(dāng)實(shí)際值分配到我想要執(zhí)行的那個(gè)操作上,這樣實(shí)在是太笨拙不堪了。
反觀Swift,它擁有一種內(nèi)置的機(jī)制,能夠在移除對(duì)樣板代碼的需求任務(wù)完成前后進(jìn)行監(jiān)控并響應(yīng),這項(xiàng)功能稱之為屬性監(jiān)視器(Property Observers)。你可以為屬性添加一個(gè)willSet或(和)一個(gè)didSet監(jiān)視器,willSet在設(shè)置新的值之前調(diào)用,而didSet則是在新的值被設(shè)置之后立即調(diào)用。
class MyView : UIView { var aSubview : UIView { didSet { oldValue.removeFromSuperview() self.addSubview(aSubview) } } init(frame: CGRect) { self.aSubview = UIView() super.init(frame: frame) } }
willSet監(jiān)視器會(huì)將新的屬性值作為固定參數(shù)傳入,在willSet的實(shí)現(xiàn)代碼中可以為這個(gè)參數(shù)指定一個(gè)名稱,如果不指定則參數(shù)仍然可用,而其默認(rèn)名稱用newValue表示。同樣,didSet監(jiān)視器也會(huì)將舊的屬性值作為參數(shù)傳入,可以為該參數(shù)命名或直接使用默認(rèn)參數(shù)名oldValue。
6. 優(yōu)雅的閉包占用列表
盡管Swift的內(nèi)存管理仍然采用自動(dòng)引用計(jì)數(shù),但其對(duì)于語(yǔ)法卻有著極大的改善。在Swift中,無(wú)需在塊以外聲明weak或unsafe_unretained變量,你可以定義一個(gè)閉包應(yīng)該如何使用捕獲列表來(lái)捕獲常量或變量。
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提供了一種頗為優(yōu)雅的方法來(lái)解決循環(huán)強(qiáng)引用所存在的問(wèn)題,稱之為閉包占用列表(Closuer Capture List)。在上段代碼中,除了可以初始化URL的DocumentViewController之外,我還定義了一個(gè)FilePickerController類。當(dāng)用戶選擇一個(gè)文件時(shí),它便會(huì)提供一個(gè)閉包成員,而DocumentViewController則有一個(gè)方法來(lái)呈現(xiàn)該文件并處理用戶選擇文件的操作。此時(shí),如果該閉包強(qiáng)烈捕獲self,則將產(chǎn)生循環(huán)強(qiáng)引用。
這種情況下,我明白只要有DocumentViewController,閉包也就會(huì)永遠(yuǎn)存在,所以我可以通過(guò)[unowned self]使用無(wú)主引用來(lái)解決循環(huán)強(qiáng)引用,這比Objective-C可要干凈得多了。
7. 構(gòu)造過(guò)程更安全
在使用Objective-C編寫(xiě)程序時(shí),我常常會(huì)犯一個(gè)錯(cuò)誤,總是要我抓狂地花上好幾分鐘去調(diào)試它。這個(gè)問(wèn)題非常普遍,就是在我想加載內(nèi)容進(jìn)去的一個(gè)類中,有一個(gè)可變數(shù)組,為了能夠被添加進(jìn)去,首先需要初始化數(shù)組,但我卻老是會(huì)忘記。
而在Swift中,編輯器將會(huì)處于兩方面的原因截獲它,其一便是你必須對(duì)取值為空的屬性進(jìn)行處理,在定義一個(gè)構(gòu)造器之后,如果你留下任何未初始化的成員變量,編譯器便會(huì)顯示錯(cuò)誤,這將節(jié)省我大量的時(shí)間,如下:
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中有一個(gè)名為指定構(gòu)造器(Designated Initializers)的概念,每個(gè)類都應(yīng)該有一個(gè)將全部類都初始化在安全狀態(tài)的構(gòu)造器,而其他構(gòu)造器則應(yīng)該調(diào)用一個(gè)指定構(gòu)造器來(lái)確保類良好,但很遺憾這在Objective-C中并不是強(qiáng)制執(zhí)行的。而Swift則將其融入編譯器,這就意味著Directory類必須調(diào)用init(path)構(gòu)造器,否則將顯示錯(cuò)誤,而在path沒(méi)有被定義的情況下,該類將無(wú)法實(shí)現(xiàn),當(dāng)類想要獲得更大的繼承時(shí),便顯得尤為重要。
現(xiàn)在,您還可以進(jìn)入Swift的mobilehub主頁(yè)進(jìn)行資源分享和討論。
本文為CSDN編譯整理,未經(jīng)允許不得轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)聯(lián)系market#csdn.net(#換成@)