Swift語言要點:Objective-C開發(fā)人員的觀點【raywenderlich中文版翻譯】

如果這周一你和我一樣,正坐下來享受keynote,興奮的開始嘗試所有全新的可愛的API。 然后你猛然聽到談?wù)撘环N新語言時,你豎起了耳朵:Swift! 它深深震撼了你,因為這不是對Objective-C的擴展,而是一種完全全新的語言。 也許你很興奮? 也許你很高興? 也許你不知道該怎么想。
Swift肯定改變了我們將來編寫iOS和Mac程序的方式。 在這篇文章中,我概述了Swift語言的一些亮點,將它們與Objective-C中的對應(yīng)部分進行了對比。
注意這篇文章不是一個Swift入門指南。 蘋果已經(jīng)發(fā)布了一本很棒的書,我強烈建議閱讀它。 相反,這里著重討論swift中值得注意和玩的特別酷的地方!

Swift-250x250.png

類型

Swift提供的第一個巨大利器是類型推斷。在使用類型推斷的語言中,程序員不需要用類型信息注釋變量。編譯器會根據(jù)賦值的變量去推斷它。例如,編譯器可以自動將此變量設(shè)置為String:

//自動推斷
var name1 =“Matt”
//顯式類型(在這種情況下是可選的)
var name2:String =“Matt”

伴隨類型推斷,Swift還提供了類型安全。在Swift中,編譯器(除了少數(shù)幾種特殊情況)都知道對象的完整類型。因為掌握更多的信息,所以編譯器可以決定關(guān)于如何編譯代碼。
這與Objective-C形成鮮明對比,Objective-C本質(zhì)上是非常動態(tài)的。在Objective-C中,編譯時并不知道類型。這是因為你可以在運行時向現(xiàn)有類添加方法,添加完全新的類,甚至修改實例的類型。
讓我們更詳細地看一看,考慮下面的Objective-C:

Person * matt = [[Person alloc] initWithName:@“Matt Galloway”];
[matt sayHello];
```
當(dāng)編譯器看到調(diào)用sayHello時,它可以檢查是否頭部中聲明了這個方法,它可以對Person上調(diào)用sayHello類型。如果沒有該方法則會報錯,但這是它只能做到這些。這通常足以捕捉引用的第一行的bug。它能捕獲類似代碼書寫錯誤的bug。但是因為動態(tài)本質(zhì),編譯器不知道sayHello是否會在運行時改變,甚至是否必須存在。比如,它可以是協(xié)議上的可選方法。 (可以用responsesToSelector:來檢查)。
由于缺乏強類型,在Objective-C中編譯器在調(diào)用方法時幾乎做不了什么做優(yōu)化。處理動態(tài)分發(fā)的方法稱為objc_msgSend。我相信你在許多回溯中都能看到這一點!在這個方法中,方法選擇器的實現(xiàn)先查找然后跳轉(zhuǎn)。這是一定會增加開銷和復(fù)雜性的。
現(xiàn)在看看Swift中的相同的代碼:
```
var matt = Person(name:“Matt Galloway”)
matt.sayHello()
```
在Swift中,編譯器對所有方法調(diào)用中的類型都了解更多。它確切地知道sayHello()被定義的位置,因此可以通過直接跳轉(zhuǎn)到實現(xiàn)替代動態(tài)分發(fā)來優(yōu)化某些調(diào)用。其他情況下,它可以使用vtable樣式分發(fā),這樣的開銷遠遠小于Objective-C中的動態(tài)分發(fā)。這類似于C ++中的虛擬函數(shù)調(diào)用。
編譯器在Swift中更能發(fā)作用。它能幫助阻止子類型相關(guān)的bug進入你的代碼庫,還能通過智能優(yōu)化使代碼運行更快。
###泛型
Swift的另一個巨大的特性是泛型。如果你熟悉C++,那么你可以認為這個就像模板。因為Swift對類型很嚴格,所以你必須聲明一個方法能夠接受某些類型的參數(shù),但有時有些同一種方法應(yīng)當(dāng)兼容多個不同類型。
這種情況的一個例子是,您希望將一對值存儲在一起,因為他們是經(jīng)常被使用的結(jié)構(gòu)體。對于蒸熟你可以在Swift中這樣實現(xiàn):
```
struct IntPair {
    讓a:Int!
    讓b:Int!
    init(a:Int,b:Int){
        self.a = a
        self.b = b
    }}
    func equal() - > Bool {
        return a == b
    }}
}}
let intPair = IntPair(a:5,b:10)
intPair.a // 5
intPair.b // 10
intPair.equal()// false
```
搞定!但現(xiàn)在你想要這個方法也適用于浮點數(shù)。你可以定義一個FloatPair類,但是她們非常相似。這時候泛型就該發(fā)揮作用了。不需要聲明一個全新的類,你可以這樣做:
```
struct Pair <T:Equatable> {
    讓a:T!
    讓b:T!
    init(a:T,b:T){
        self.a = a
        self.b = b
    }}
    func equal() - > Bool {
        return a == b
    }}
}}
let pair = Pair(a:5,b:10)
pair.a // 5
pair.b // 10
pair.equal()// false
let floatPair = Pair(a:3.14159,b:2.0)
floatPair.a // 3.14159
floatPair.b // 2.0
floatPair.equal()// false
```
很棒!現(xiàn)在你可能還不太清楚為什么你需要這個特性,但相信我:用到機會是數(shù)不清的。你很快就會看到自己在代碼中用到這些。
###容器
你已經(jīng)知道并喜歡NSArray,NSDictionary和他們的可變類型。好了,現(xiàn)在你將要了解它們在Swift中的對應(yīng)物,幸運的是他們很相似。下面是如何聲明數(shù)組和字典:
```
let array = [1,2,3,4]
let dictionary = [“dog”:1,“elephant”:2]
```
盡管有細微的差別,但你還是應(yīng)該很熟悉。在Objective-C中,只要你愿意,數(shù)組和字典可以同時包含任何類型。但在Swift中,數(shù)組和字典都是被指定類型,而且是通過泛型來指定類型!
上面的兩個變量可以像下面這樣重寫它們的類型表達(即使實際上你不必這樣做!):
```
let array:Array <Int> = [1,2,3,4]
let dictionary: Dictionary<String, Int> = ["dog": 1, "elephant": 2]
```
注意如何使用泛型定義存儲在容器中的內(nèi)容。它還有一個更簡短,可讀性更強的形式,但本質(zhì)相同:
```
let array:[Int] = [1,2,3,4]
let dictionary:[String:Int] = [“dog”:1,“elephant”:2]
```
注意,您不能把非Int類型的內(nèi)容添加到數(shù)組中。這聽起來不怎么友好,但其實非常有用。API不再需要記錄什么被存儲在數(shù)組中,它由某個方法返回或存儲在屬性中。編譯器可以立即獲取這些信息,以便它可以更好地檢查錯誤和優(yōu)化。
###可變類型
Swift中集合的一個有趣之處在于它們的可變類型。數(shù)組和字典沒有“可變”對應(yīng),而是使用標(biāo)準let和var。對于那些還沒有讀過這本書,或者深入到Swift中的人(我建議你盡快去讀!),let用于聲明常量,var用于聲明變量,變量! let就像在C / C ++ / Objective-C中使用const。
與集合相關(guān)的方式是使用let聲明的集合不能更改大小。也就是說,它們不能添加到或移除元素。如果你試圖這么做,那么你會收到報錯,如:
```
let array = [1,2,3]
array.append(4)
// error: immutable value of type 'Array<Int>' only has mutating members named 'append'
```
這同樣適用于dictionary。這就允許編譯器推斷集合,并進行適當(dāng)?shù)膬?yōu)化。例如,大小不能改變,則存儲這些值的存儲器不需要為了適應(yīng)新值被重新分配空間。因此,申請一個不可變的集合時應(yīng)當(dāng)使用let。
###字符串
Objective-C中對字符串的處理真是臭名昭著。即使簡單的任務(wù),如連接許多不同的值都非常乏味。請參見以下示例:
```
Person *person = ...;
NSMutableString * description = [[NSMutableString alloc] init];
[description appendFormat:@“%@ is%i years old.”,person.name,person.age];
if(person.employer){
  [description appendFormat:@“They work for%@.”,person.employer];
} else {
  [description appendString:@" They are unemployed."];
}
```
這是相當(dāng)冗長乏味的,并且包含了很多與被操作的數(shù)據(jù)無關(guān)的字符。在Swift中將會是這樣:
```
var description =“”
description + =“\(person.name)is \(person.age)years old?!?if person.employer { 
   description += " They work for \(person.employer)."
} else { 
   description += " They are unemployed."
}
```
非常清晰!注意這個按格式創(chuàng)建字符串的更簡潔的方法,你現(xiàn)在可以通過使用+=連接字符串,不再有可變字符串和不可變字符串。
Swift中另一個很棒的地方是字符串的比較。在Objective-C中使用==比較字符串的相等性是不對的,而是應(yīng)該用isEqualToString:方法。這是因為前者用于比較指針相等。 Swift刪除這個中間的級別,能夠直接使用==來比較字符串。這也意味著字符串可以在switch語句中使用。相關(guān)的更多問題在下一節(jié)討論。
最后一個好消息是,Swift本身支持完整的Unicode字符集。你可以在字符串中使用任何Unicode代碼點,甚至方法和變量名!只要你愿意甚至可以有一個方法叫??(一坨屎?。?另一個好消息是,現(xiàn)在有一個內(nèi)置的方法來計算字符串的真實長度。當(dāng)涉及到完整的Unicode范圍時,字符串長度的計算是不重要的。因為有的字符超過1個字節(jié),所以你不能只是說它是在UTF8中存儲字符串的字節(jié)數(shù)。在Objective-C中,使用2字節(jié)對對來存儲字符串,所以NSString通過計算UTF16的數(shù)量進行計算,但這在技術(shù)上不是正確的,因為一些Unicode代碼點占用兩個2字節(jié)對。
幸運的是,Swift有一個方便的方法來計算字符串中代碼點的真實數(shù)量,是叫做countElements()的上層方法。你可以這樣用:
```
var poos =“\ u {1f4a9} \ u {1f4a9}”
countElements(poos)// 2
```
但它仍然不適用于所有情況,它只是計算Unicode代碼點的數(shù)量,不考慮改變其他字符的特殊代碼點。例如,可以在先前的字符上放一個變音符號。這種情況下,countElements()將返回2,即使它看起來像只有1個字符。像這樣:
```
var eUmlaut =“e \ u {0}”
countElements(eUmlaut)// 2
```
通過以上這些,相信你會同意Swift中的字符串是棒的!
###Switch語句
在Swift的簡短介紹中,我想要提到的最后一件事是switch語句。相比較于Objective-C中的這部分,Swift中已經(jīng)有了顯著的改進。這非常有趣,因為基于Objective-C是C的嚴格超集,這些改變不可能加到Objective-C中。
第一個令人興奮的功能是關(guān)于字符串的Switch。這可能是你之前想做卻不能做的事情,要在Objective-C中“switch”字符串,你必須使用大量帶有isEqualToString的if語句,就像這樣:
```
if ([person.name isEqualToString:@"Matt Galloway"]) { 
   NSLog(@"Author of an interesting Swift article");
} else if ([person.name isEqualToString:@"Ray Wenderlich"]) { 
   NSLog(@"Has a great website");
} else if ([person.name isEqualToString:@"Tim Cook"]) {
   NSLog(@"CEO of Apple Inc.");
} else {
   NSLog(@"Someone else);
}
```
這要打很多字,可讀性也不高。然而Swift中是這樣:
```
switch person.name { 
     case "Matt Galloway": 
         println("Author of an interesting Swift article") 
     case "Ray Wenderlich":
         println("Has a great website") 
     case "Tim Cook": 
         println("CEO of Apple Inc.") 
     default: 
         println("Someone else")
}
```
除了關(guān)于字符串的Switch,注意這里還有一些有趣的地方,沒有break。這是因為Switch中的case不會再執(zhí)行下一個,不會出現(xiàn)意外出錯的情況!
準備好,下面的switch語句很可能會打擊你!
```
switch i {
case 0, 1, 2:
        println("Small")
case 3...7:
        println("Medium")
case 8..<10:
        println("Large")
case _ where i % 2 == 0:
        println("Even")
case _ where i % 2 == 1:
        println("Odd")
default:
        break
}
```
這里出現(xiàn)了break。因為Switch需要是窮盡的,即需要能夠處理所有情況。這種情況下,希望default不做任何事情,所以添加了一個斷點來聲明不應(yīng)該發(fā)生任何事情。
下一個有趣的事情是你看到的...和.. <。這些是新的運算符,用于定義范圍。前者,定義了直到并包括右邊界的范圍。后者定義了一個范圍,直到不包括右邊界。這些都非常有用。
最后一件事是定義case作為輸入的計算。這種情況下,如果值不匹配從零到十的任何值,則打印“Even”(如果是偶數(shù))和“Odd”(如果它是奇數(shù))。神奇!
###未來發(fā)展
這里只是希望給你體驗一下Swift語言和它的奇妙之處。 但還有更多! 閱讀蘋果的書和[其他](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/index.html)蘋果[文檔](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-XID_26),將幫助你學(xué)習(xí)這種新的語言。 你遲早要這么做的!
此外,如果你想了解更多關(guān)于Swift,請查看我們[三個Swift的新書](https://www.raywenderlich.com/?page_id=74805) - 涵蓋你需要知道的關(guān)于Swift,iOS 8等等!
我們很想聽聽你對Swift語言的看法,或者如果你有令人興奮的亮點。 請聯(lián)系我!

[原文鏈接](https://www.raywenderlich.com/73997/swift-language-highlights) 本文為個人翻譯,不用與任何商業(yè)用途,如需轉(zhuǎn)載請與本人聯(lián)系。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容