第一部分Welcome to Swift 第二章:A Swift Tour
因爲英文文檔各位看官手裏都有。所以我這裏就不在佔用篇幅來粘貼英文文檔了。直接上譯文:
Swift 概述
在編程傳統中,第一個程序是在屏幕上打印一句“Hello World”。在Swift中,這句話可以使用一句話來完成:
如果你使用過C或者OC語言來編寫代碼。那麼看起來這個語法你很熟悉。在Swift中,這行代碼是一個完整的程序。你並不需要爲輸入/輸出或者字符串處理功能來導入一個單獨的庫。寫在全球範圍的代碼都作爲函數的入口點,所以你並不需要一個main函數,也不必在每個語句的末尾寫分號。
這次概覽通過展示如何完成各種程序任務來給你足夠的信息開始編寫Swift代碼,如果你有不明白的地方,不要擔心,這個概覽裏的一切細節都會在本書後面進行詳細解釋。
注:爲獲得最佳體驗,在Xcode裏把本章作爲一個playground打開,playground允許你編輯代碼清單,並立即看到結果。
1.簡單值
使用let 來創建一個常量,使用var來創建一個變量。一個常量的值並不需要在編譯時是已知的,但你必須給它分配一個值一次,這意味着你可以使用常量來命名一個你決定在許多地方使用的值。
var myVariable = 42
myVariable = 50
let myConstant = 42
常量或變量必須有相同的類型,你要分配給它的值。然而,你並不總是必須明確寫出類型。提供一個值,當您創建一個常量或變量可以讓編譯器推斷出其類型。在上面的示例中,編譯器推斷出myVariable是整數,因爲它的初始值是一個整數。
如果初始值沒有提供足夠的信息(或者如果沒有初始值),可以在改變後製定類型,用冒號隔開。
let implicitInteger = 70
let implicitDouble = 70.0
let explicitDouble: Double = 70
嘗試:創建一個常數,明確爲float類型,並且值爲4
值永遠不會轉換爲另一種類型。如果你需要一個值轉換爲不同的類型,明確地創建一個所需類型的實例。
let label = "The width is "
let width = 94
let widthLabel = label + String(width)
嘗試:嘗試從最後一行除去String的轉換,你得到了什麼錯誤?
還有一個更簡單的方法包含字符串值:把值寫在括號中 ,並在括號前寫一個反斜線(\)。例如:
let apples = 3
let oranges = 5
let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit.
嘗試:使用(\),在一個string裏包含一個浮點計算,並在一個greeting裏包含某個人的名字。
使用方括號([])創建數組和字典,並在括號中寫出index和key來訪問它們的元素。
var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"
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.控制流
使用 if 和 swith 來書寫條件語句。使用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 { ... }是一個錯誤的語句。
結合if和let,可以方便的處理可空變量(nullable variable)。對於空值,需要在類型聲明後添加?顯式標明該類型可空。
var optionalString: String? = "Hello"
optionalString == nil
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
嘗試:將optinalName設爲nil,greeting的值將會怎樣?添加一個else,當optinalName爲nil時爲greeting設置一個不同的值。
如果if語句可選的值爲nil時,條件控制將爲false,括號中的代碼也將被跳過。否則,可選的值將會在常量let後分配給該常量。
Switch支持任何類型的數據和各種各樣的比較操作,它們並不侷限於整數或者判斷等於。
let vegetable = "red pepper"
switch vegetable {
case "celery":
let vegetableComment = "Add some raisins and make ants on a log."
case "cucumber", "watercress":
let vegetableComment = "That would make a good tea sandwich."
case let x where x.hasSuffix("pepper"):
let vegetableComment = "Is it a spicy \(x)?"
default:
let vegetableComment = "Everything tastes good in soup."
}
嘗試:刪掉default case,將會出現什麼錯誤?
執行了相對應的swith case中的方法後,程序會從swith中退出,並不會執行下一個case,所以沒必要在每個case裏添加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) in interestingNumbers {
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
除了傳統的for循環之外,也可以通過結合..(生成一個區間)和for-in實現同樣的邏輯。
var firstForLoop = 0
for i in 0..3 {
firstForLoop += i
}
firstForLoop
var secondForLoop = 0
for var i = 0; i < 3; ++i {
secondForLoop += 1
}
secondForLoop
使用..確定一個範圍並忽略它的上限值,同時也可以使用..來確定一個範圍,幷包含這兩個值。
3.函數和閉包
使用func來定義一個函數,按照其名稱和括號內的參數列表來調用該函數,使用->來定義函數的返回值類型和名稱。
func greet(name: String, day: String) -> String {
return "Hello \(name), today is \(day)."
}
greet("Bob", "Tuesday")
嘗試:刪除 day參數,在greeting中添加一個“今天的午餐”的參數。
使用一個元組來從函數中返回多個值。
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 makeIncrementer() -> (Int -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)
一個函數可以接受另一個函數作爲它的一個參數。
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)
函數實際上是閉包的一種特殊情況。您可以通過使用大括號({})周圍的代碼寫一個沒有名字的閉包。使用單獨的參數和從身體返回類型。
numbers.map({
(number: Int) -> Int in
let result = 3 * number
return result
})
嘗試:寫一個返回所有奇數都爲0的閉包。
你可以通過不同的方式讓閉包更加簡潔,當一個閉包的類型是已知的,如回調委託,可以省略它的參數的類型,它的返回類型,或兩者兼而有之。
numbers.map({ number in 3 * number })
閉包的參數可以用數字而不是按引用參數名稱 -這樣的做法是對很短的閉包特別有用。通過作爲最後一個參數傳遞給函數的閉包可以在括號後立即出現。
sort([1, 5, 3, 12, 2]) { $0 > $1 }
4.對象和類
使用 class 後跟類名來創建一個類。聲明類的屬性與變量或常量的聲明方式相同,只不過它是在thecontext類中。同樣,方法和函數的聲明方式也相同。
class Shape {
var numberOfSides = 0
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
嘗試:使用 let 增加一個常量屬性,並添加一個方法來接受一個參數。
通過類名後跟括號來創建類的實例,使用點語法來訪問該實例的屬性和方法。
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
這個版本的Shape類缺少了一個很重要的東西:在設置初始化時,這個類已經被創建了。應使用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."
}
}
通過init構建對象,既可以使用self顯式引用成員字段(name),也可以隱式引用(numberOfSides)。
如果你的對象在被釋放之前需要做一些清理工作,那麼要使用deinit來創建一個reinitializer。
子類在類名後應包含他們超類的類名。Swift支持繼承和多態(override父類方法)。如果這裏的simpleDescription方法沒有被標識爲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.想要改變父類中定義的屬性的值,可以使用除setter或getter方法之外的自定義方法。
如果不需要計算屬性的值,但需要在賦值前後進行一些操作的話,使用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 。另外,可選值是未包裝的,所有 "?" 之後的都作爲未包裝的值。在兩種情況中,整個表達式的值是可選值。
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength