Swift要點:從Objective-C開發者的角度看Swift

 

代碼環境是Xcode6.3-Beta3.html

Swift已經極大的改變了開發iOS應用的方式。本文中,我會列出Swift的幾個重點,而且和Objective-C一一作出對比。ios

注意,本文不是Swift的入門指導。蘋果發佈了Swift Programming Language,強烈建議您讀一讀這本書。文本主要介紹Swift中比較酷炫的特性。web

類型

Swift提供的第一個重大的改進是類型推斷。使用了類型推斷的編程語言,開發者不須要在聲明中明確指定變量的類型。編譯器會自動探知要賦給變量的值的類型。好比下面的例子,編譯器會自動設定變量的類型爲String:編程

// 類型推斷
var str = "Hello World!"

// 明肯定義類型,這裏能夠不這樣
var swift: String = "Hello Swift"

和類型推斷一塊兒帶來的是類型安全。在Swift中,編譯器(通常來講所有,可是在不多的狀況下)知道一個類型的所有類型。這樣給編譯器一個選擇如何編譯代碼的機會,由於編譯器有足夠的信息。swift

這承托出了Objective-C的一個很是動態的本質。在Objective-C中,任何類型在編譯期間都是未知的。這也是爲何你能夠在運行時給已經存在的類添加方法,添加一個全新的類型,甚至於改變一個對象的類型。數組

看看Objective-C的代碼:安全

Person *man = [[Person alloc] init];
[man sayHello];

當編譯器看到對方法sayHello的調用的時候,它就會檢查類型Person類的頭文件中是否有一個叫作sayHello的方法。若是沒有就報錯。編譯器就只能夠作這些。這確實能夠你可能引入的最初簡單的bug:如拼寫錯誤。可是,由於動態性,編譯器不可能知道sayHello方法是否會在運行改變、甚至是否存在。這多是一個協議中得optional方法。好比(還記得那些使用respondsToSelector的檢查嗎)。app

因爲缺少強類型, 在Objective-C中調用方法的時候編譯器幾乎不能作什麼優化。處理代碼動態分發的方法是:objc_msgSend。我肯定你在不少的時候見過這個方法。在這個方法中,尋找selector並調用。你不能說這沒有增長複雜度。編程語言

再回頭看看Swift的實現:函數

var p = Person()
p.sayHello()

在Swift中,編譯器在調用方法的時候知道類型的更多信息。它準確的知道sayHell方法在哪裏定義的。所以,Swift能夠在方法調用的時候直接調轉到方法定義的地方,而無需通過任何的動態分發過程。在其餘的狀況下,Swift也可使用vtable風格的分發方式,比Objective-C的動態分發花費小得多了。這也是C++的虛方法使用的分發機制。

Swift的編譯器也很是的有用。能夠排除細微的類型相關的bug。它也能使你的代碼運行的更快。

泛型

Swift帶來的另外一個很大的改變是泛型。若是你對C++很熟悉的花,那麼你能夠把這些和C++的template作類比。由於Swift是類型嚴格的,你必須方法能夠接受的參數的類型。可是有的時候你的方法對於不一樣的類型來講做用是相同的。

好比,你但願把一對值存放在一塊兒。你能夠用Swift實現存放整型數值的結構:

struct IntPair {
    let a: Int!
    let b: Int!
    
    init(a: Int, b: Int){
        self.a = a
        self.b = b
    }
    
    func equal() -> Bool {
        return a == b
    }
}

let intPair = IntPair(a: 1, b: 1)
println("\(intPair.a)" + "\(intPair.b)" + " equal: \(intPair,equal())")

Sort of useful. But now you want this to work for floating point numbers as well. You could define aFloatPair class, but that would look awfully similar. This is where generics come in. Instead of declaring a whole new class, you could simply do this:

有點用處,可是你也想讓這個能夠用於浮點數。你能夠定義一個FloatPair類, 可是實在是太類似了。這個時候泛型就會發揮用處了。相對於定義一個全新的類型,你能夠這樣:

struct Pair<T: Equatable> {
    let a: T!
    let b: T!
    
    init(a: T, b: T){
        self.a = a
        self.b = b
    }
    
    func equal() -> Bool {
        return a == b
    }
}

let tempPair = Pair<Int>(a: 1, b: 1)
var tempA = tempPair.a
var tempB = tempPair.b
var tempEqual: Bool = tempPair.equal()

println("\(tempA) " + "\(tempB) " + "equal: \(tempEqual)")

let tempPair1 = Pair<Float>(a: 1.0, b: 1.0)
var tempA1 = tempPair1.a
var tempB1 = tempPair1.b
var tempEqual1: Bool = tempPair.equal()

println("\(tempA1) " + "\(tempB1) " + "equal: \(tempEqual1)")

請各位讀者原諒我用了這麼爛得命名方式啊。。。

很是有用!你可能不是很清楚你爲何須要泛型,可是相信我會有不少時候你會用到。你很快會發如今哪裏可使用這些代碼。

容器

也許你以爲NSArray和NSDictionary和對應的可變類型都已經很好用了。可是你須要學習對應的Swift容器。幸運的是,他們很是類似。如:

let array = [1, 2, 3, 4, 5]
let dictionary = ["dog": 1, "elephant": 2]

這些對你來講已經很是熟悉了。瞅一眼就能夠搞定的。在Objective-C中,數組和字典可hi包含你想要的任何類型。可是在Swift中數組和字典是強類型的。並且這些容器都是泛型類型的。

上面的兩個變量可使用類型表達式來重寫(你不是必需要這麼作),好比:

let array: Array<Int> = [1, 2, 3, 4, 5]
let dictionary: Dictionary<String, Int> = ["dog": 1, "elephant": 2]

Notice how generics are used to define what can be stored in the container. There is also a short form for these, which is slightly more readable, but essentially boils down to the same thing:

注意泛型是怎麼定義什麼能夠放進容器中的。定義的方式也有簡短的方式的,可讀性更好。可是本質上來講是同樣的:

let array: [Int] = [1, 2, 3, 4, 5]
let dictionary: [String, Int] = ["dog": 1, "elephant": 2]

注意,這個時候你沒法給數組添加任何的不是Int類型的值。這看起來很差,可是很是有用。你再也不須要在文檔中專門指明屬性返回的數組中存放的是什麼類型的值。編譯器會自動檢測錯誤並優化代碼的編譯。

可變性

Swift容器的一個有意思的地方是他們都是可變的(mutable)。array和dictionary沒有「mutable」的對應類型了。可變和不可變都由letvar關鍵字控制。let就是用來聲明一個常量的關鍵字,var是用來聲明一個變量的。let就像是C、C++或者Objective-C中得const關鍵字同樣。

The way this relates to collections is that collections declared using let cannot change size. That is, they cannot be appended to, or removed from. If you try to, then you get an error like so:

使用let聲明的集合不能再修改size,也就是不能再append或者remove。若是你這麼作了,就會報錯:

let array: [Int] = [1, 2, 3, 4, 5]
array.append(33)
//immutable value of type '[int]' only has mutating members named 'append'

字典也一樣使用這個規則。這容許編譯器推測出這一類型的集合並做出相應的優化。若是size不能修改,集合的存儲實現再也不須要從新分配新的空間容納新的值。所以,常用let來定義再也不改變的集合是一個很好的實踐。

字符串

Objective-C的字符串很是難用。即便是簡單的拼接字符串之類的都須要寫不少的代碼。好比:

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."];
}

This is quite tedious and contains a lot of characters that are nothing to do with the data being manipulated. The same in Swift would look like this:

這樣的寫法很是的冗長,並且包含了與本次操做無關的其餘字符。一樣的功能在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."
}

很是的清晰。更加清晰的建立一個字符串,你可使用字符串拼接操做符+=。再也不須要可變字符串和非可變字符串。

另外一個Swift字符串的變化是」字符串的比較「。你知道在Objective-C中不能使用==來比較兩個字符串的相等。你得用isEqualToString方法。這是應爲==是比較的指針是否相等。Swift中能夠直接使用==比較兩個字符串。也就是說字符串能夠在switch-case表達式中。更多相關請繼續收看下一節內容。

Swift的字符串的另外一個好東西是本地支持Unicode。你可使用任意的Unicode字符,即便是在變量名和函數名中。若是你願意你能夠聲明一個方法💩 (pile of poo!)

另外一個字符串的好東西是一種內建的方法計算一個字符串的長度。當字符串所有都是Unicode字符的時候,計算字符串的長度就變得重要了。你不能只是說有多少個byte用於存放UTF8字符串了。由於有的字符一個byte是不夠的。在Objective-C中,NSString經過UTF16的個數來計算字符串的長度,2個byte存放一個字符串。可是,有的Unicode字符一次須要用兩個2個byte的組合,因此以前NSString的計算方式再也不能正確的代表字符串中字符的個數。

Fortunately, Swift has a handy function to calculate the true number of code-points in a string. It uses the top level function called countElements(). You would use it like so:

幸運的是,Swift有一個很好用的方法能夠計算字符串的字符個數。這須要用到一個全局的countElements()方法。使用方法:

var poos = "\u{1f4a9}\u{1f4a9}"
//countElements(poos) // 2
count(poos) // 2

在Swift1.2中使用count方法

如你所見,Swift的字符串真的是很是好用!

Switch表達式

本文的最後須要簡單的介紹一下Swift的switch表達式。Swift的switch表達式對Objective-C的switch表達式進行了極大的升級。由於,不打破Objective-C這個C語言的超集的狀況是不能有大的升級的。

The first exciting feature is switching on strings. This is something that you may have wanted to do before, but couldn’t. To 「switch」 on strings in Objective-C you had to use lots of if-statements withisEqualToString: like so:

第一個就是switch表達式可使用字符串。這也許是你早就想要的一個功能。在Objective-C中你不得不使用不少的if-else表達式和isEqualToString方法,好比:

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")
}

除了在switch上使用string之外,還有一些有意思的地方。swtich表達式中是沒有break的。這是由於Swift的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
}

因此,再也不有break的問題。

下一個有意思的地方是...和..<。這些新的操做符是用來定義range的。前面的而一個是包含右邊的數字。後面的一個是不包含右邊的數字。這很是有用。

接下來學習什麼?

但願本文能讓你對Swift語言讓人興奮的特性有一個認識。可是還有更多的東西須要學習。你能夠看蘋果的文檔,這回幫助你更好的學習這個新的語言。若是你以爲翻譯的又什麼問題請點擊這裏查看原文。

相關文章
相關標籤/搜索