A Swift Tour, 蘋果新推出的開發語言

蘋果近日召開了全球開發者大會(WWDC),推出了一種新的開發語言 Swift,有人說是用來替代Objective-C的,以下是蘋果官方文檔,第一時間整理了一下,覺得還是很有前景的,有些英文看不懂的就直接複製了,接下來的時間還是要持續關注呢。

官方鏈接:

https://developer.apple.com/library/prerelease/ios/referencelibrary/GettingStarted/LandingPage/index.html#//apple_ref/doc/uid/TP40014345


https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.html#//apple_ref/doc/uid/TP40014097-CH2-XID_1


簡介

Swift是iOS和OS X應用編程的一種新的編程語言,基於C和Objective-C,卻沒有C的一些兼容約束。Swift採用了安全的編程模式和添加現代的功能來讓編程更加簡單、靈活和有趣。界面基於廣大人民羣衆愛戴的Cocoa和Cocoa Touch框架,展示了軟件開發的新方向。

Swift已經存在了多年。Apple基於已有的編譯器、調試器、框架作爲其基礎架構。通過ARC(Automatic Reference Counting,自動引用計數)來簡化內存管理。我們的框架棧則一直基於Cocoa。Objective-C進化支持了塊、collection literal和模塊,允許現代語言的框架無需深入即可使用。

Objective-C開發者會感到Swift的似曾相識。Swift採用了Objective-C的命名參數和動態對象模型。提供了對Cocoa框架和mix-and-match的互操作性。基於這些基礎,Swift引入了很多新功能和結合面向過程和麪向對象的功能。

Swift對新的程序員也是友好的。他是工業級品質的系統編程語言,卻又像腳本語言一樣的友好。他支持playground,允許程序員實驗編寫Swift代碼功能並立即看到結果,而無需麻煩的構建和運行一個應用。


A Swift Tour

許多編程語言一開始都以hello world來開個好頭,用Swift來寫,只有一行:

println("Hello, world")

如果你有C/C++,Objective-C的基礎,看起來應該會很熟悉,在Swift裏,這一行就是一個完整的代碼了,不需要再引入其他的庫文件了,全局的代碼就已經是程序的入口了,所以也不需main方法,甚至也不需要分號結尾

1.Simple Values(簡單值)

使用let定義常量,var定義變量,常量並不需編譯時制定,但至少需要賦值一次,意味着只需定義一次常量,就可在所有地方使用

var myVariable = 42
myVariable = 50
let myConstant = 42

上面例子中,編譯自動推斷myVariable是個整數類型,不需要顯示聲明,在以後的賦值也要賦予相同的類型

如果在初始化的時候沒有提供足夠的信息或根本沒有初始化賦值,可以在後面聲明,用冒號分割

let implicitInteger = 70
let implicitDouble = 70.0
let explicitDouble: Double = 70

已經賦的值不會隱式轉換爲其他類型,如果需要轉換爲其他類型,需要明確寫出構造所需類型的實例

let label = "The width is "
let width = 94
let widthLabel = label + String(width)
還有一種更簡單的寫法在字符串中顯示變量值,變量值寫入小括號中,並在前面添加反斜線

let apples = 3
let oranges = 5
let appleSummary = "I have \(apples)apples."
let fruitSummary = "I have\(apples + oranges) pieces of fruit."

用方括號[] 創建數據或字典,通過下標或鍵值訪問元素

var shoppingList =["catfish", "water", "tulips", "bluepaint"]
shoppingList[1] = "bottle ofwater"
 
var occupations = [
   "Malcolm": "Captain",
   "Kaylee": "Mechanic",
]
occupations["Jayne"] ="Public Relations"

如果想要創建一個空的數組或字典,使用初始化語句

let emptyArray = String[]()
let emptyDictionary =Dictionary<String, Float>()

如果不確定類型,可以直接寫成 [] 表示空數組,[:]表示空字典

 shoppingList = []   //Went shopping and bought everything.


2.Control Flow(控制流)

使用if和switch作爲條件控制,使用for-in,for,while和do-while來循環,小括號不是必須的,但大括號是必須的

let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
   if score > 50 {
       teamScore += 3
   } else {
       teamScore += 1
   }
}
teamScore

在if語句中,條件必須是布爾表達式,意味着 if score{…} 是錯誤的,不能隱式的與0比較

可以同時使用if和let來防止變量值的丟失,這些值是可選的,可選值可包含一個值或一個nil來指定值是否存在,類型後面的(?) 表示值是可選的

var optionalString: String? ="Hello"
optionalString == nil
 
var optionalName: String? = "JohnAppleseed"
var greeting = "Hello!"
if let name = optionalName {
   greeting = "Hello, \(name)"
}

如果可選的值是nil,那麼條件爲false的語句將會跳過,Otherwise,the optional value is unwrapped and assigned to the constant after let, which makes the unwrapped value available insidethe block of code.

 

switch支持多種數據以及多種比較,不侷限於整數和測試數相等

let vegetable = "red pepper"
switch vegetable {
case "celery":
   let vegetableComment = "Add some raisins and make ants on alog."
case "cucumber","watercress":
   let vegetableComment = "That would make a good tea sandwich."
case let x wherex.hasSuffix("pepper"):
   let vegetableComment = "Is it a spicy \(x)?"
default:
   let vegetableComment = "Everything tastes good in soup."
}

switch在匹配之後就會跳出程序塊,繼續執行下一個邏輯代碼,而不需要break語句來跳出

 

使用for-in來遍歷字典中的每一個值,提供一對名稱來爲每個鍵值對使用

let interestingNumbers = [
   "Prime": [2, 3, 5, 7, 11, 13],
   "Fibonacci": [1, 1, 2, 3, 5, 8],
   "Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) ininterestingNumbers {
   for number in numbers {
       if number > largest {
            largest = number
       }
   }
}
largest

使用while來重複執行一代碼塊直到條件成立,循環的條件可放在while末尾來確保循環至少執行一次

var n = 2
while n < 100 {
   n = n * 2
}
n
 
var m = 2
do {
   m = m * 2
} while m < 100
m


可在循環中定義一個索引,通過..來表示索引所表示的範圍,以下兩個例子做同樣的事情

var firstForLoop = 0
for i in 0..3 {
   firstForLoop += i
}
firstForLoop
 
var secondForLoop = 0
for var i = 0; i < 3; ++i {
   secondForLoop += 1
}
secondForLoo

使用 .. 構建的範圍不包含最大值,使用 … 構建的範圍大小值都包含

 

3.Functions and Closures(函數與閉包 )

使用func聲明一個函數,調用函數的方法是使用函數名稱加括號裏的參數列表,使用->分割參數名和返回的類型

func greet(name: String, day: String) -> String {
   return "Hello \(name), today is \(day)."
}
greet("Bob","Tuesday")

使用tuple可以返回多個值

func getGasPrices() -> (Double,Double, Double) {
   return (3.59, 3.69, 3.79)
}
getGasPrices()

函數參數個數可以不固定,用數組表示

func sumOf(numbers: Int...) -> Int {
   var sum = 0
   for number in numbers {
       sum += number
   }
   return sum
}
sumOf()
sumOf(42, 597, 12)

函數可以嵌套,內嵌函數可訪問其定義所在函數的變量

func returnFifteen() -> Int {
   var y = 10
   func add() {
       y += 5
   }
   add()
   return y
}
returnFifteen()

函數是第一類型的,意味着函數也可以返回另一個函數

func hasAnyMatches(list: Int[],condition: Int -> Bool) -> Bool {
   for item in list {
       if condition(item) {
            return true
       }
   }
   return false
}
func lessThanTen(number: Int) ->Bool {
   return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(numbers, lessThanTen)

一個函數可以接收其他函數作爲參數

func hasAnyMatches(list: Int[],condition: Int -> Bool) -> Bool {
   for item in list {
       if condition(item) {
            return true
       }
   }
   return false
}
func lessThanTen(number: Int) -> Bool{
   return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(numbers, lessThanTen)

函數其實就是特殊形式的閉包,寫一個閉包不需要命名,只要加在大括號中,用in取參數和方法體的返回值

numbers.map({
   (number: Int) -> Int in
   let result = 3 * number
   return result
})

有很多種方式來寫一些更簡單的閉包,當一個閉包的類型是已知的,比如說對委託的回調,可以省略類型的參數和返回值,單一語句的閉包可以直接把值返回

numbers.map({ number in 3 * number })

可以用數字取代名字來引用一個參數,對短的閉包是很有用的,A closure passed as the last argument to a function can appearimmediately after the parentheses.

 sort([1, 5, 3, 12, 2]) { $0 > $1 }


4.Objects and Classes(對象和類)

使用class 加類名來創建一個類,類中屬性的聲明和常量及變量的聲明是一樣的,除了這是在類的context中,方法和函數的聲明也是一樣的

class Shape {
   var numberOfSides = 0
   func simpleDescription() -> String {
       return "A shape with \(numberOfSides) sides."
   }
}

在類名的後面添加小括號來創建類的實例,使用點操作符訪問類的屬性和方法

var shape = Shape()
shape.numberOfSides = 7
var shapeDescription =shape.simpleDescription()

使用init來創建一個構造器來在創建實例的時候初始化

class NamedShape {
   var numberOfSides: Int = 0
   var name: String
   
   init(name: String) {
       self.name = name
   }
   
   func simpleDescription() -> String {
       return "A shape with \(numberOfSides) sides."
   }
}

Notice how self is used todistinguish the name property from the name argument to the initializer. Thearguments to the initializer are passed like a function call when you create aninstance of the class. Every property needs a value assigned—either in itsdeclaration (as with numberOfSides) or in the initializer (as with name).

如果需要在對象銷燬時進行清理工作,使用deinit創建析構器

子類包含其父類的名稱,以冒號分隔,在繼承標準子類時不需要聲明,所以可以根據需要包括或者忽略父類

子類的方法可通過override重載父類的方法,沒有的話會編譯錯誤,編譯器也會檢查那些沒有被重載的方法

class Square: NamedShape {
   var sideLength: Double
   
   init(sideLength: Double, name: String) {
       self.sideLength = sideLength
       super.init(name: name)
       numberOfSides = 4
   }
   
   func area() ->  Double {
       return sideLength * sideLength
   }
   
   override func simpleDescription() -> String {
       return "A square with sides of length \(sideLength)."
   }
}
let test = Square(sideLength: 5.2,name: "my test square")
test.area()
test.simpleDescription()


屬性也有getter和setter方法

class EquilateralTriangle: NamedShape {
    var sideLength: Double = 0.0
    
    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 3
    }
    
    var perimeter: Double {
    get {
        return 3.0 * sideLength
    }
    set {
        sideLength = newValue / 3.0
    }
    }
    
    override func simpleDescription() -> String {
        return "An equilateral triagle with sides of length \(sideLength)."
    }
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
triangle.perimeter
triangle.perimeter = 9.9
triangle.sideLength

在perimeter的setter方法中,新的值得命名就是newValue,可以在set之後提供一個不衝突的名稱

注意EquilateralTriangle類構造有三個步驟:

1.    設置屬性的值

2.    調用父類的構造器

3.    改變父類定義的值,Any additional setup work that uses methods, getters, or setters canalso be done at this point.

 

如果不需要計算屬性但卻需要賦值後才能繼續執行,可以使用willSet和didSet,一下例子保證三角形的邊長等於矩形的邊長

class TriangleAndSquare {
   var triangle: EquilateralTriangle {
   willSet {
       square.sideLength = newValue.sideLength
   }
   }
   var square: Square {
   willSet {
       triangle.sideLength = newValue.sideLength
   }
   }
   init(size: Double, name: String) {
       square = Square(sideLength: size, name: name)
       triangle = EquilateralTriangle(sideLength: size, name: name)
   }
}
var triangleAndSquare =TriangleAndSquare(size: 10, name: "another test shape")
triangleAndSquare.square.sideLength
triangleAndSquare.triangle.sideLength
triangleAndSquare.square =Square(sideLength: 50, name: "larger square")
triangleAndSquare.triangle.sideLength

函數和類的方法有一個不同點,函數的參數名僅用於函數,方法中的參數名也可以用來調用方法(除了第一個參數),缺省時,一個方法有一個同名的參數,調用時就是使用參數本身,可以指定第二個名字,在方法內部使用

class Counter {
   var count: Int = 0
   func incrementBy(amount: Int, numberOfTimes times: Int) {
       count += amount * times
   }
}
var counter = Counter()
counter.incrementBy(2, numberOfTimes:7)

當使用可選值時,可以像方法屬性一樣在操作符前使用問號(?),如果值本來就是nil,那所有在?之後的代碼將會忽略,整個表達式都是nil,另外,可選值是unwrapped的,所有在?後的代碼都作爲unwrapped值,在兩種情況下,真個表達式都是可選值

let optionalSquare: Square? =Square(sideLength: 2.5, name: "optional square")
let sideLength =optionalSquare?.sideLength

 

5.Enumerations and Structures(枚舉和結構)

使用enum來創建枚舉,跟類和其他命名一樣,枚舉也可以定義方法

enum Rank: Int {
   case Ace = 1
   case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
   case Jack, Queen, King
   func simpleDescription() -> String {
       switch self {
       case .Ace:
            return "ace"
       case .Jack:
            return "jack"
       case .Queen:
            return "queen"
       case .King:
            return "king"
       default:
            return String(self.toRaw())
       }
   }
}
let ace = Rank.Ace
let aceRawValue = ace.toRaw()

以上例子中,初始值的枚舉類型爲Int,那麼只需要指定第一個初始值,其餘的會自動分配,還可以使用字符串或浮點數作爲初始值的枚舉。

 

使用toRaw和fromRaw函數可以轉換初始值和枚舉值

if let convertedRank = Rank.fromRaw(3){
   let threeDescription = convertedRank.simpleDescription()
}

枚舉中成員就是實際使用的值了,而不是其他方式寫的初始值,事實上,如果不提供第一個初始值的話,這種情況纔是初始值

enum Suit {
   case Spades, Hearts, Diamonds, Clubs
   func simpleDescription() -> String {
       switch self {
       case .Spades:
            return "spades"
       case .Hearts:
            return "hearts"
       case .Diamonds:
            return "diamonds"
       case .Clubs:
            return "clubs"
       }
   }
}
let hearts = Suit.Hearts
let heartsDescription =hearts.simpleDescription()

Notice the two ways that theHearts member of the enumeration is referred to above: When assigning a valueto the hearts constant, the enumeration member Suit.Hearts is referred to byits full name because the constant doesn’t have an explicit type specified.Inside the switch, the enumeration is referred to by the abbreviated form.Hearts because the value of self is already known to be a suit. You can usethe abbreviated form anytime the value’s type is already known.

 

使用struct來創建結構體,結構體支持多個與類相同的行爲,包括方法和構造器,區別是代碼之間總是使用拷貝(值傳遞),而類是傳遞引用

struct Card {
   var rank: Rank
   var suit: Suit
   func simpleDescription() -> String {
       return "The \(rank.simpleDescription()) of\(suit.simpleDescription())"
   }
}
let threeOfSpades = Card(rank: .Three,suit: .Spades)
let threeOfSpadesDescription =threeOfSpades.simpleDescription()

一個枚舉的實例成員可以擁有實例的值。相同枚舉成員實例可以有不同的值。你在創建實例時賦值。指定值和原始值的區別:枚舉的原始值與其實例相同,你在定義枚舉時提供原始值。

例如,假設情況需要從服務器獲取太陽升起和降落時間。服務器可以響應相同的信息或一些錯誤信息

enum ServerResponse {
   case Result(String, String)
   case Error(String)
}
 
let success =ServerResponse.Result("6:00 am", "8:09 pm")
let failure =ServerResponse.Error("Out of cheese.")
 
switch success {
case let .Result(sunrise, sunset):
   let serverResponse = "Sunrise is at \(sunrise) and sunset is at\(sunset)."
case let .Error(error):
   let serverResponse = "Failure... \(error)"
}

注意日出和日落時間實際上來自於對 ServerResponse 的部分匹配來選擇的

 

6.Protocols and Extensions(協議和擴展)

使用protocol來聲明一個協議

protocol ExampleProtocol {
   var simpleDescription: String { get }
   mutating func adjust()
}


協議可以被類、枚舉和結構使用

class SimpleClass: ExampleProtocol {
   var simpleDescription: String = "A very simple class."
   var anotherProperty: Int = 69105
   func adjust() {
       simpleDescription += "  Now100% adjusted."
   }
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription
 
struct SimpleStructure: ExampleProtocol{
   var simpleDescription: String = "A simple structure"
   mutating func adjust() {
       simpleDescription += " (adjusted)"
   }
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription

注意,在SimpleStructure聲明中使用mutating關鍵字用於標誌這個方法的話會修改他的結構,而SimpleClass中並不需要設置mutaing標誌,因爲類中的方法會修改這個類

 

使用extension去添加已有的類型,如新方法和計算值,可以使用extension,保證任意類型具有相同的協議,不管這個類型是從框架還是類庫中來的

extension Int: ExampleProtocol {
   var simpleDescription: String {
   return "The number \(self)"
   }
   mutating func adjust() {
       self += 42
   }
}
7.simpleDescription

可以像其他命令類型一樣使用協議命名,比如創建一個對象集合有不同的類型,但所有類型都符合同一個協議,當使用一些協議類型的值時,在協議外部定義的方法是不可用的

let protocolValue: ExampleProtocol = a
protocolValue.simpleDescription
// protocolValue.anotherProperty  // Uncomment to see the error

Even though the variableprotocolValue has a runtime type of SimpleClass, the compiler treats it as thegiven type of ExampleProtocol. This means that you can’t accidentally accessmethods or properties that the class implements in addition to its protocolconformance.

 

7.Generics(泛型)

在尖括號裏的名字就定義了一個泛型的函數或類型

func repeat<ItemType>(item:ItemType, times: Int) -> ItemType[] {
   var result = ItemType[]()
   for i in 0..times {
       result += item
   }
   return result
}
repeat("knock", 4)

泛型也可以用於函數和方法,類、枚舉及結構

// Reimplement the Swift standardlibrary's optional type
enum OptionalValue<T> {
   case None
   case Some(T)
}
var possibleInteger:OptionalValue<Int> = .None
possibleInteger = .Some(100)

在類型後面使用where來指定一個需求列表,比如指定某個泛型必須實現某種協議,或要求兩種類型必須相同,或要求某個類必須具有一個父類

func anyCommonElements <T, U whereT: Sequence, U: Sequence, T.GeneratorType.Element: Equatable,T.GeneratorType.Element == U.GeneratorType.Element> (lhs: T, rhs: U) ->Bool {
   for lhsItem in lhs {
       for rhsItem in rhs {
            if lhsItem == rhsItem {
                return true
            }
       }
   }
   return false
}
anyCommonElements([1, 2, 3], [3])

一般情況下可以忽略where,在括號後面寫一個協議名或泛型 Writing<T: Equatable> is the same as writing <T where T: Equatable>.



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章