跳轉到內容

Swift 簡介 / Swift 基礎

來自 Wikibooks,開放世界中的開放書籍

Swift 基礎

[編輯 | 編輯原始碼]

在本章中,您將學習如何使用 Swift[1] 的基本概念,包括變數、常量和陣列。本章涵蓋的另一個重要主題是如何在 Swift 中編寫函式和類。

變數和常量

[編輯 | 編輯原始碼]

與大多數其他語言一樣,Swift 使用變數來儲存值並透過唯一的名稱引用它們。這些變數的值可以是可變的或不可變的。建議在值的程式碼中不需要更改時使用常量。這也使您編寫的程式碼更安全。

宣告變數

[編輯 | 編輯原始碼]

在 Swift 中,變數使用關鍵字 var 宣告。建議僅在以後需要更改變數的值時才使用 var

var greeting = "Good morning!"

宣告常量

[編輯 | 編輯原始碼]

如果不需要更改變數的值,則可以使用關鍵字 let 宣告它。

let answerToEverything = 42

型別標註

[編輯 | 編輯原始碼]

型別標註可用於明確常量或變數可以儲存哪些型別的值。如果不存在初始值,則需要型別標註。如果提供了初始值,Swift 會推斷變數應該具有哪種型別。

var greeting: String				
//Declaring a single variable	

let alpha, beta, gamma: Double 		
//Declaring 3 variables in one line

更改值

[編輯 | 編輯原始碼]

如果應該更改變數的值,則它必須與原始值的型別相同。在本例中,greeting 的值更改為另一個 String "Good evening"。

var greeting = "Good morning!"				
greeting = "Good evening!"	
//greeting has now the value "Good evening!"		
let pi = 3.1415
pi = 3.2
// Compile-time error: This value cannot be changed


資料型別

[編輯 | 編輯原始碼]

型別安全和型別推斷

[編輯 | 編輯原始碼]

Swift 是一種型別安全的語言,這意味著例如,無法將數字分配給儲存 String 的變數,也無法使用錯誤型別的引數呼叫函式。在編譯時,Swift 會執行型別檢查,只有在不存在型別不匹配的情況下才會完成編譯。

如果您在未指定型別的情況下宣告變數或常量,Swift 會使用型別推斷來找出適當的型別。如果您在宣告變數時提供浮點數,Swift 會推斷它為 Double 型別的值。

let absolutZero = -273.15				
// absolutZero is inferred to be of type Double

整數是像 45 或 -37 這樣的整數。它們沒有小數部分,並且是有符號或無符號的。如果整數是有符號的,它可以是零、正數或負數。無符號整數只能是零或正數。Swift 提供 8、16、32 和 64 位形式的整數型別。雖然可以選擇特定大小,例如 UInt8Int32,但在大多數情況下使用 Int 型別。它的大小與平臺的本機字長相同。在現代作業系統中,它的大小為 64 位。Uint 是一種無符號整數型別,其大小也與平臺的字長相同。

浮點數

[編輯 | 編輯原始碼]

具有小數部分的浮點數可以表示比 Int 的可能值大得多或小得多的值。諸如 -0.0001 或 19.99 之類的數字可以儲存在 DoubleFloat 型別的變數中。Double 是一種 64 位浮點數,其精度至少為 15 位小數。Float 提供 6 位小數的精度。

布林值

[編輯 | 編輯原始碼]

在 Swift 中,布林值資料型別稱為 Bool,它可以具有常數值 truefalse

var switchedOn = true				
// switchedOn is now true and of type Boolean
switchedOn = false

布林值非常適合用於控制流操作,例如 if 語句

if switchedOn{				
    print("The light is on!")
    // Will be executed if switchedOn = true
}
else{
    print("It's dark as a dungeon!")
}

可選型別

[編輯 | 編輯原始碼]

可選變數有兩種可能的狀態:存在值或不存在值。要宣告具有可選資料型別的變數,請使用 ?

var salary: Double?				
// salary is set to nil automatically and contains no value
salary = 1923.12
// salary now has a value but is still of type Double?
salary = nil
// salary is set back to nil and contains no value

要確定值是否已設定,可以使用 if 語句以及 ==(等於)或 !=(不等於)運算子。

if salary == nil{				
    print("Salary for this employee has not been set yet!")
} else {
    print("This employee's salary is \(salary!)")
}


在上面的示例中,您可以在可選名稱的末尾看到一個感嘆號。它用於解包可選的值以使用它。如果對不包含值的可選使用 !,將觸發執行時錯誤。

可選繫結

[編輯 | 編輯原始碼]

可選繫結檢查可選型別是否已設定,如果已設定,則將其值設定為一個新的臨時可用的變數或常量。

if let netSalary = salary{				
    print("This emplyee's net salary is \(netSalary*0.85)")
} else {
    print("Salary for this employee has not been set yet!")
}

新的常量 netSalary 僅在 if 語句中可用,不需要解包。

集合型別

[edit | edit source]

在 Swift 中,有三種主要的集合型別。

  • 陣列用於儲存有序的值集合
  • 集合用於儲存無序的唯一值集合
  • 字典用於儲存無序的鍵值對

就像變數和常量一樣,所有三種集合型別都是型別安全的,這意味著無法插入錯誤型別的的值。這種型別安全性的積極方面是,您始終知道從集合中獲取的值的型別。

分配給變數的陣列、集合和字典是可變的,這意味著在建立後可以新增、更改或刪除值。如果需要不可變的集合型別,則必須將其分配給常量。

陣列

[edit | edit source]

陣列提供相同型別元素的有序列表。有兩種方法可以宣告一個數組,Array<Element>[Element]。Element 是應該儲存在陣列中的值的型別。陣列可以為空建立,也可以填充值建立。也可以使用預設值建立陣列。

var intArray = Array<Int>()				
// creates an empty Integer Array
var preinitialized = Array(repeating: 1, count: 5)
print(preinitialized)
// prints "[1, 1, 1, 1, 1]"

陣列也可以使用陣列字面量初始化。這是一個用逗號分隔的值列表。

let fibonacci = [1,1,2,3,5,8,13]				
// creates an immutable Integer Array

陣列的屬性和方法

[edit | edit source]
intArray.append(9)				
// Adds '9' to intArray

intArray += [1,2,3]
// emptyInt now contains 9, 1, 2 and 3

print(intArray.count)				
// prints the number of elements in the array, 4

if(intArray.isEmpty){
    print("I'm an empty Array! :( ")
} else {{
    print("I'm storing \(intArray.count) elements!")
}

下標語法用於訪問陣列中的元素。索引從零開始,這意味著要訪問陣列的第一個元素,您必須在陣列的名稱中新增 [0]。

var firstValue = intArray[0]				
// firstValue is now 9

集合

[edit | edit source]

集合儲存相同型別的的值,並在專案的順序不重要時使用。集合還確保不出現重複的值。只有當值可雜湊時,才能將它們儲存在集合中。Swift 的基本型別,如 String、Int 和 Bool,預設情況下都是可雜湊的。

	var devices = Set<String>()	
	// creates a new empty set	

	devices.insert("iPhone")	
	print(devices)	
	// prints ["iPhone"]	

	var webSuffixes: Set<String> = [".html", ".js", ".css"]	
	print(webSuffixes)	
	// prints [".js", ".html", ".css"]	

	print("There are \(webSuffixes.count) common suffixes in web development.")	
	// prints There are 3 common suffixes in web development.	

	if webSuffixes.contains(".py"){	
	    print("Yeah, Python made it to the top 3 :)")	
	} else {	
	    print("Python is not in the top 3 :( ")	
	}	
	// prints "Python is not in the top 3 :( "	

	for suffix in webSuffixes{	
	    print ("\(suffix)")	
	}	
	// .css	
	// .js	
	// .html

集合提供了大量的集合運算。這些運算實現了數學集合論中最重要的規則。

  • 交集
  • 對稱差
  • 並集
  • 減法

以下程式碼片段展示了這些運算的工作原理。

let favSongs: Set = ["Enter Sandman", "Bohemian Rapsody", "Blitzkrieg Bop", "Painkiller"]
let songsFrom90s: Set = ["Raining Blood","Enter Sandman","Painkiller","Wonderwall"]

var playList = Set<String>()
playList = favSongs.union(songsFrom90s)
/* union combines the values of both sets, values which are in both sets will be displayed once.
 
["Blitzkrieg Bop", "Raining Blood", "Bohemian Rapsody", "Enter Sandman", "Painkiller", "Wonderwall"]*/

playList = favSongs.intersection(songsFrom90s)
/* intersect creates a new list which contains all values that exist in both Sets ["Enter Sandman", "Painkiller"] */

playList = favSongs.symmetricDifference(songsFrom90s)
/* symmetricDifference stores all values of both sets into a new set, except those which were in both sets.
["Blitzkrieg Bop", "Raining Blood", "Bohemian Rapsody", "Wonderwall"]*/

playList = favSongs.subtracting(songsFrom90s)
/*subtracting creates a new set which only includes values which are not in the second set. ["Blitzkrieg Bop", "Bohemian Rapsody"] */

字典

[edit | edit source]

字典在無序集合中儲存鍵值對。鍵是值的唯一識別符號。與陣列類似,有兩種方法可以宣告字典。Dictionary<Key, Value>[Key: Value]。此外,字典也可以使用字典字面量建立。

var offeredStudies = [String: String]()
// creates an empty [String: String] dictionary

offeredStudies["SWD"] = "Software-Design"
offeredStudies["ITM"] = "Internettechnik"
// adds two key-value pairs to the dictionary

offeredStudies["ITM"] = "Internettechnik und Mediendesign"
// changes the value of "ITM"

print(offeredStudies)
// prints ["SWD": "Software-Design", "ITM": "Internettechnik und Mediendesign"]

var locations: [String: String] = ["K": "Kapfenberg", "G": "Graz", "B": "Bad Gleichenberg"]
// creates a dictionary with a dictionary literal

print("FH Joanneum has \(locations.count) locations in Styria.")
// prints "FH Joanneum has 3 locations in Styria."


for (shortcut, location) in locations{
    print("\(shortcut) is a shortcut for \(location)")
// G is a shortcut for Graz
// K is a shortcut for Kapfenberg
// B is a shortcut for Bad Gleichenberg
}


控制流

[edit | edit source]

Swift 提供了許多方法來控制程式碼的執行方式。

For-In 迴圈

[edit | edit source]

使用 For-In 迴圈是一種簡單的方法,可以遍歷陣列、集合或任何其他型別的序列或範圍。

let grades: [Int: String] = [1: "Sehr Gut", 2: "Gut", 3: "Befriedigend", 4: "Genuegend", 5: "Nicht genuegend"]for (grade, word) in grades.sorted(by: <){
    print("\(grade) is a \"\(word)\"")
}
// 1 is a "Sehr Gut"
// 2 is a "Gut"
// 3 is a "Befriedigend"
// 4 is a "Genuegend"
// 5 is a "Nicht genuegend"

在典型的 For-In 迴圈中,計數器始終增加 1。如果您想要進行更小或更大的步長,則必須使用 stride。

let max = 100
for quarters in stride(from: 25, to: max, by: 25){
    print(quarters)
}
// 25
// 50
// 75

While 迴圈

[edit | edit source]

While 迴圈非常有用,如果您不知道需要多少次迭代。只要條件為真,while 迴圈就會執行語句。有兩種型別的 while 迴圈。

  • while 在執行語句之前檢查條件是否為真。
  • repeat-while 執行語句,並在最後檢查條件是否仍然為真。
var cardValues = [Int]()
for value in 1...10{
    cardValues.append(value)
// creates an array with values 1 to 10 - simulates a card deck
}

var bankCount = 0
while bankCount < 21 {
    var randomIndex = Int(arc4random_uniform(UInt32(cardValues.count)))
    // creates a random number - simulates picking a random card
    bankCount += cardValues[randomIndex]
    print(bankCount)
    
    if(bankCount > 21){
        print("The bank loses!")
    }
    else if(bankCount == 21){
        print("The bank wins!")
    }
}

如果

[edit | edit source]

if 語句是決定應執行哪些語句的最簡單方法,基於某些條件。如果只有少數可能的條件,則使用它。if 語句可以獨立存在,但最好始終提供 else 語句,因為它使程式碼更易讀和理解。=== Switch ===

var switchedOn = true

if(switchedOn == true){
    print("All lights are on")
} else {
    print("Can someone please turn on the light?")
}

還可以為不止一個條件提供不同的程式碼路徑

var score = 88;

if(score > 90 && score <= 100){
    print("Perfect!")
} else if(score > 80 && score <= 90){
    print("Good Job!")
} else if(score > 70 && score <= 80){
    print("Not Bad!")
} else if(score > 60 && score <= 70){
    print("Puh, that was close!")
} else{
    print("Good luck next time")
}

如您所見,隨著條件數量的增加,會產生大量重複的程式碼。可以使用 switch 語句來減少重複程式碼的數量。

開關

[edit | edit source]

switch 語句通常有幾種可能的條件情況。所有 switch 語句都必須是窮舉的,這意味著條件的每個可能值都必須有一個情況。因此,應該提供一個預設語句,如果這些情況都不匹配,則會執行該語句。

var grade = "A"
switch grade{
case "A":
    print("Excellent")
case "B":
    print("Above average")
case "C":
    print("Satisfactory")
case "D":
    print("Below Average")
case "F":
    print("Failure")
default:
    print("Test not attempted")
}


函式

[edit | edit source]

函式是程式碼的重要組成部分。它們具有標識名稱 - 最好使用描述函式功能的名稱 - 在呼叫函式時使用該名稱。它們可以有零到多個引數。這些輸入值在呼叫函式時傳遞。

定義和呼叫函式

[edit | edit source]

在下面的程式碼片段中,您可以看到函式的定義以 fund 開頭,後面是名稱和可選的引數列表。-> 運算子指定此函式的返回型別。沒有箭頭定義的函式沒有返回值。

func combineStrings(begin: String, end: String) -> String{
    let combinedString = begin + end
    return combinedString
}

print(combineStrings(begin: "Let's get ", end: "swifty!"))
// prints "Let's get swifty!"

引數和返回值

[edit | edit source]

函式可以有零到多個引數,也可以有零到多個返回值。在下面的示例中,您可以看到一個函式,它以兩個整數值作為引數,並返回兩個整數值。第二個函式沒有引數,也沒有返回值。

func sumAndDifference(value1: Int, value2: Int) -> (sum: Int, difference: Int){
    let sum = value1 + value2
    let difference = value1 - value2
    return (sum, difference)
}

func printTimestamp(){
    let date = Date() //gets the current date
    let calendar = Calendar.current
    let hour = calendar.component(.hour, from: date)
    let minutes = calendar.component(.minute, from: date)
    print(String(hour) + ":" + String(minutes))
}

printTimestamp()
// prints hours and minutes whenever it is called
print(sumAndDifference(value1: 10, value2: 5))
// prints "(sum: 15, difference: 5)"

引數標籤和引數名稱

[edit | edit source]

在 Swift 中,引數有一個引數標籤,在呼叫函式時使用它,還有一個引數名稱,在實現中使用它。

func getsFined(allowed speed: Double, measured value: Double) -> String {
    if(speed < value){
        return "Driver was too fast - that's gonna be expensive"
    }
    else{
        return "Good boy"
    }
}

print(getsFined(allowed: 100, measured: 120))
// prints "Driver was too fast - that's gonna be expensive"

也可以編寫沒有引數標籤的函式。

func add2Numbers(_ number1: Int, _ number2: Int) ->Int{
    return number1 + number2
    
}
print(add2Numbers(4,8))
// 12

可變引數

[edit | edit source]

這些引數接受相同型別的可變數量的引數。當您不知道要傳遞給函式的引數的確切數量,或者所需引數的數量從一個函式呼叫到另一個函式呼叫而變化時,它特別有用。

func calcCart(_ prices: Double...) -> String {
    var sum: Double = 0
    for price in prices{
        sum += price
    }
    return String(sum)
}

print("The items in your cart cost " + calcCart(10.99, 9.99, 5.69))
// prints "The items in your cart cost 26.67"

函式型別

[edit | edit source]

函式的型別包括引數型別和它的返回型別。讓我們看看上面程式碼片段中其中一個函式的函式型別。

func getsFined(allowed speed: Double, measured value: Double) -> String

此函式由兩個型別為 Double 的引數和一個型別為 String 的返回值組成。因此,函式型別為 (Double, Double) -> String

函式也可以有函式型別作為返回值。

func calcTaxFood(_ prices: Double...) -> String {
    var sum: Double = 0
    for price in prices{
        sum += price*0.1
    }
    return String(sum)
}

func calcTaxNonFood(_ prices: Double...) -> String {
    var sum: Double = 0
    for price in prices{
        sum += price*0.2
    }
    return String(sum)
}

func chooseTaxCalculator(isFood: Bool) ->(Double...) -> String {
    return isFood ? calcTaxFood : calcTaxNonFood
    // if isFood is true, calcTaxFood will be returned 
    // it it is false, calcTaxNonFood will be returned
}
let taxFood = chooseTaxCalculator(isFood: true)(19.99, 12.99, 6.79)
let taxNonFood = chooseTaxCalculator(isFood: false)(9.99, 1.99, 14.99)
print("You paid " + taxFood + "Euro taxes for your food and " + taxNonFood + "Euro for the rest.")

巢狀函式

[edit | edit source]

在另一個函式中定義的函式稱為巢狀函式。它們在函式外部不可見。但是,如果封閉函式返回它們,則可以在函式外部使用它們。

func itemCounter(incoming: Bool) -> (Int) -> Int {
    
    func increaseCount(count: Int) -> Int{
        print("\(count) items were added to our current stock")
        return itemsOnStock + count
    }
    
    func decreaseCount(count: Int) -> Int{
        print("\(count) items were shipped to customers")
        return itemsOnStock - count
    }
    
    return incoming ? increaseCount : decreaseCount
}

var itemsOnStock = 8
let itemsIncoming = itemCounter(incoming: true)
let itemsOutgoing = itemCounter(incoming: false)


print("There are \(itemsOnStock) items in the warehouse")
// There are 8 items in the warehouse
itemsOnStock = itemsIncoming(10)
// 10 items were added to our current stock
itemsOnStock = itemsOutgoing(7)
// 7 items were shipped to customers
print("There are \(itemsOnStock) items in the warehouse")
// There are 11 items in the warehouse

類和結構

[edit | edit source]

作為面向物件的語言,Swift 也提供了類,它是物件或例項的構建計劃,以及結構,這是一種類似的結構。介面用於使類或結構可供其他程式碼部分使用,它們會自動提供。

類和結構共享許多功能,例如

  • 屬性用於儲存值
  • 方法提供功能
  • 兩者都可以擴充套件
  • 初始化器用於設定其初始狀態

但是,有些功能只有類提供

  • 繼承
  • 在執行時檢查和解釋類的型別
  • 類可以使用解構函式釋放資源

類還是結構?

[edit | edit source]

在決定類或結構哪種更適合您的需求之前,需要考慮這兩種結構的一些特徵。其中一個最重要的區別是,類總是按引用傳遞,而結構按值傳遞。

Apple 建議[2] 在以下情況下使用結構

  • 結構的主要目的是封裝一些簡單的數值。
  • 合理預期這些值將被複制,而不是被引用。
  • 結構中的所有屬性都是值型別。
  • 結構不需要從其他現有型別繼承屬性或行為。

在下面的程式碼片段中,您可以看到兩個結構,SoftTyre 和 HardTyre,它們儲存描述輪胎特徵的值。如您所見,只儲存了簡單的值,例如整數和布林值。Racecar 類也包含一些簡單的數值,例如重量或車隊名稱,但也包含 SlickTyre 結構的例項。

struct DryTyre{
    var forWetCondition = false
    var grip = 3
    var durability = 3
    
}
struct WetTyre{
    var forWetCondition = true
    var grip = 4
    var durability = 2
}

class Racecar{
    let teamName = "Red Bull Racing"
    var tyre = DryTyre()
    var weightEmpty = 650
    var weightWithDriver = 728
}

訪問屬性

[edit | edit source]

可以使用點語法訪問類和結構的屬性。

var car = Racecar()
// create an instance
print("\(car.weightEmpty)")
// prints "650"
car.weightWithDriver = 732
// assign a new value using dot syntax

print("This tyre suits for wet conditions: \(car.tyre.forWetCondition)\nand has a durability value of: \(car.tyre.durability)")
// This tyre suits for wet conditions: false
// and has a durability value of: 3

結構型別的逐成員初始化

[edit | edit source]

可以使用自動生成的逐成員初始化器來初始化新結構例項的屬性。

let superSoft = SoftTyre(forWetCondition: false, grip: 4, durability: 2)
// create and initialize a new instance of the SoftTyre struct
car.tyre = superSoft
print("This tyre has a durability value of: \(car.tyre.durability)")
// This tyre has a durability value of: 2

值型別與引用型別

[edit | edit source]

結構、列舉和 Swift 中的所有基本型別,例如整數、字串和陣列,都是值型別,這意味著當它們被傳遞給函式時,值會被複制。對函式內部複製的整數值進行的更改不會影響外部的原始值。

let ultraSoft = SoftTyre(forWetCondition: false, grip: 5, durability: 1)

var tyre = ultraSoft
tyre.durability = 4
print("Durability of tyre is now \(tyre.durability)")
// Durability of tyre is now 4
print("Durability of ultraSoft ist still \(ultraSoft.durability)")
// Durability of ultraSoft ist still 1

類是引用型別,這意味著當它們被傳遞給函式時,不會被複制。相反,會使用對已存在例項的引用。在下面的程式碼片段中,Racecar 的例項被分配給名為 rb13 的常量。在分配屬性 raceWins 和 weightEmpty 後,rb13 被分配給一個新的常量 rb14。由於 Racecar 類的例項是按引用傳遞的,因此 rb14 中的更改會自動影響 rb13 中的屬性。

let rb13 = Racecar()
rb13.raceWins = 39
rb13.weightEmpty = 680

let rb14 = rb13
rb14.raceWins = 42
rb14.weightEmpty = 700

print("rb13 now also has \(rb13.weightEmpty) kg and \(rb13.raceWins) wins")
// rb13 now also has 700 kg and 42 wins

引用

[edit | edit source]
  1. Apple Inc. | 2017 | Swift 程式語言 | [線上][訪問:2017 年 9 月 18 日] | https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/index.html#//apple_ref/doc/uid/TP40014097-CH3-ID0
  2. Apple Inc. | 2017 | Swift - 類和結構 | [線上][訪問:2017 年 9 月 18 日] | https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html#//apple_ref/doc/uid/TP40014097-CH13-ID82
華夏公益教科書