Swift語言階段學(xué)習(xí)小結(jié)

Swift-楊夏

前言:原本這周應(yīng)該寫Swift第三周學(xué)習(xí)總結(jié)了,但是由于我已完成關(guān)于這門語言的基礎(chǔ)學(xué)習(xí),索性就做一個(gè)關(guān)于Swift語言階段比較系統(tǒng)和完善的總結(jié),在這里跟大家分享和交流,亮點(diǎn)多多,不容錯(cuò)過!

語言基礎(chǔ)

程序是指令的集合,寫程序就是寫一系列的指令去控制計(jì)算機(jī)做我們想做的事
編譯:將程序設(shè)計(jì)語言轉(zhuǎn)換成計(jì)算機(jī)所能夠理解的機(jī)器語言或者某種中間代碼的過程

馮諾依曼體系結(jié)構(gòu)的計(jì)算機(jī):
1.使用二進(jìn)制
2.程序存儲(chǔ)執(zhí)行

變量和常量

定義變量和常量是為了保存數(shù)據(jù),變量和常量就是某種類型的值的存儲(chǔ)空間

var a: Int = 10
a = 100
var b: Int
b = 1000
var c = 10000

let d: Int = 10
//d = 100       // compiler error
let e = 1000

說明:1.Swift有非常強(qiáng)大的類型推斷,所以定義變量和常量時(shí)如果可以的話直接使用類型,不用手動(dòng)指定類型;2.如果可以的話應(yīng)該盡可能的使用常量而不是變量

語言元素

var a: Int = 10

關(guān)鍵字:有特殊含義的單詞
標(biāo)識(shí)符:給變量、常量、函數(shù)、類、結(jié)構(gòu)、協(xié)議、枚舉、方法、屬性等起的名字

  1. 字母(Unicod字符)、數(shù)字、下劃線,數(shù)字不能開頭
  2. 大小寫敏感(區(qū)分大小寫)
  3. 不能使用關(guān)鍵字做標(biāo)識(shí)符
  4. 使用駝峰命名法(命名變量、常量、函數(shù)、方法、屬性第一個(gè)單詞小寫,從第二個(gè)單詞開始每個(gè)單詞首字母大寫;命名類、結(jié)構(gòu)、協(xié)議、枚舉每個(gè)單詞首字母都要大寫)
  5. 見名知意
  6. 命名私有的屬性和方法時(shí)以下劃線開頭

運(yùn)算符: Swift中的運(yùn)算符其實(shí)都是函數(shù)

  1. 賦值運(yùn)算符:=、+=、-=、......
  2. 算術(shù)運(yùn)算符:+、-、*、/、%
  3. 比較運(yùn)算符:==、!=、<、<=、>、>=
  4. 邏輯運(yùn)算符:&&、||、!
  5. 條件運(yùn)算符:?:
  6. 其它運(yùn)算符:[]、.、??、?、!

字面(常)量

  1. 整數(shù)字面量: 10、1_123_456、0x10、0o10、0b10
  2. 小數(shù)字面量: 123.45、1.234e2、0xab.cdp2
  3. 字符字面量: "c"、"\n"、"\u{41}"、"\u{9a86}"
  4. 字符串字面量:"hellow"、"caf\u{e9}"
  5. 布爾字面量:true、false
  6. 空值字面量:nil
  7. 類型字面量:String.self、UILable.self

分隔符:將不同的語言元素符號(hào)分開

說明:Swift中每個(gè)語句后面的分號(hào)是可寫可不寫的,寫代碼時(shí)盡量保證一行只有一條語句這樣就可以省略掉分號(hào)

分支和循環(huán)

分支

  • if...else...
//輸入年、月判斷該月有多少天
print("請(qǐng)輸入年和月用空格隔開(例如: 1980 2)", terminator: "")
let year = inputInt()
let month = inputInt()
let day: Int
if month < 1 || month > 12 {
    print("你的輸入有誤!")
    exit(0) // 程序退出不再執(zhí)行
}
else if month == 2 {
    day = year % 4 == 0 && year % 100 != 0 || year % 400 == 0 ? 29 : 28
}
else if month == 4 || month == 6 || month == 9 || month == 11 {
    day = 30
}
else {
    day = 31
}
print("\(year)年\(month)月有\(zhòng)(day)天")
  • switch...case...default
//搖骰子做游戲
let face = Int(arc4random_uniform(6)) + 1
switch face {
case 1:
    print("扮鬼臉")
case 2:
    print("唱一首")
case 3:
    print("親一個(gè)")
case 4:
    print("真心話")
case 5:
    print("大冒險(xiǎn)")
default:
    print("脫一件")

循環(huán)

  • while
//0-100的求和
var i = 1
var sum = 0
while i <= 100 {
    sum += i
    i += 1
}
print("1 + 2 + ... + 100 = \(sum)")
  • repeat...while...
//0-100的求和
var i = 1
var sum = 0
repeat {
    sum += i
    i += 1
} while i <= 100
print("1 + 2 + ... + 100 = \(sum)")
  • for
//0-100的求和
var sum = 0
for i in 1...100 {
    sum += i
}
print("1 + 2 + ... + 100 = \(sum)")

窮舉法:窮盡所有可能性直到找到正確答案。

下面的程序?qū)崿F(xiàn)了"百錢百雞"問題求解。

問題說明:
公雞5元1只、母雞3元1只、小雞1元3只,現(xiàn)在有100元買100只雞,請(qǐng)列出所有可能的結(jié)果
for x in 0...20 {
    for y in 0...33 {
        let z = 100 - x - y
        if 5 * x + 3 * y + z / 3 == 100 && z % 3 == 0 {
            print("公雞: \(x), 母雞: \(y), 小雞: \(z)")
        }
    }
}

在循環(huán)中可以使用break關(guān)鍵字來提前終止循環(huán),也可以使用continue關(guān)鍵字使循環(huán)直接進(jìn)入下一輪,但是應(yīng)該盡量減少對(duì)break和continue的使用,因?yàn)樗鼈儾粫?huì)讓你的程序變得更好。

綜合案例
Craps賭博游戲

游戲介紹:
搖兩個(gè)骰子第一次;7或11玩家勝,2或3或12莊家勝,其他點(diǎn)(記錄)-游戲繼續(xù)
第二次及以上:搖出第一次記錄的點(diǎn)數(shù),玩家勝或者搖出7點(diǎn)莊家勝
import Foundation
func roll() -> Int {
    return Int(arc4random_uniform(6)) + 1
}
var money = 1000
repeat {
    print("玩家總資產(chǎn): ¥\(money)元")
    var debt: Int
    repeat {
        print("請(qǐng)下注: ", terminator: "")
        debt = inputInt()
    } while debt <= 0 || debt > money

    var needsGoOn = false
    let firstPoint = roll() + roll()
    print("玩家搖出了\(firstPoint)點(diǎn)")
    switch firstPoint {
    case 7, 11:
        money += debt
        print("玩家勝!!!")
    case 2, 3, 12:
        money -= debt
        print("莊家勝!!!")
    default:
        needsGoOn = true    // 游戲繼續(xù)
    }

    while needsGoOn {
        let currentPoint = roll() + roll()
        print("玩家搖出了\(currentPoint)點(diǎn)")
        if currentPoint == 7 {
            money -= debt
            print("莊家勝!!!")
            needsGoOn = false
        }
        else if currentPoint == firstPoint {
            money += debt
            print("玩家勝!!!")
            needsGoOn = false
        }
    }
} while money > 0
print("你破產(chǎn)了!!!")

容器

數(shù)組

  • 創(chuàng)建數(shù)組
var array1: [Int] = []
var array2: Array<Int> = []
var array3 = [1, 2, 3, 4, 5]
var array4 = [Int](count: 5, repeateValue: 0)
var array5 = Array<Int>(count: 5, repeateValue: 0)
  • 添加元素
array1.append(2)
array1.append(3)
array1.insert(1, atIndex: 0)
array1.insert(4, atIndex: array1.count)
array1 += [5]
array1 += [6, 7, 8]
  • 刪除元素
array1.removeAtIndex(2)
array1.removeFirst()
array1.removeFirst(2)
array1.removeLast()
array1.removeRange(1...2)
array1.removeAll()
  • 修改元素
array1[0] = 100
array1[array1.count - 1] = 500
print(array1)
  • 遍歷數(shù)組
  1. 方式1
for i in 0..<array1.count {
    print(array1[i])
}
  1. 方式2
for temp in array1 {
    print(temp)
}
for temp in array1[1...3] {
    print(temp)
}

說明:for-in循環(huán)是一個(gè)只讀循環(huán),這也就意味著再循環(huán)的過程中不能對(duì)數(shù)組中的元素進(jìn)行修改

  1. 方式3
for (i, temp) in array1.enumerate() {
    print("\(i).\(temp)")
}

提醒:操作數(shù)組是最主要的是不要越界訪問元素。數(shù)組對(duì)象的count屬性表明了數(shù)組中有多少個(gè)元素,那么有效的索引(下標(biāo))范圍是0到count - 1.

數(shù)組中的元素也可以是數(shù)組,因此我們可以構(gòu)造多維數(shù)組。最常見的是二維數(shù)組,它相當(dāng)于是一個(gè)有行有列的數(shù)組,數(shù)組中的每一個(gè)元素代表一行,該數(shù)組中的每個(gè)元素代表行里面的列。二維數(shù)組可以模擬現(xiàn)實(shí)世界中的表格、數(shù)學(xué)上的矩陣、棋類游戲的棋盤、2D游戲中的地圖,所以在實(shí)際開發(fā)中使用非常廣泛。

下面的程序是用二維數(shù)組模擬表格的例子。

func randomInt(min: UInt32, max: UInt32) -> Int {
    return Int(arc4random_uniform(max - min + 1) + min)
}

let namesArray = ["關(guān)羽", "張飛", "趙云", "馬超", "黃忠"]
let coursesArray = ["語文", "數(shù)學(xué)", "英語"]

var scoresArray = [[Double]](count: namesArray.count, repeatedValue: [Double](count: coursesArray.count, repeatedValue: 0))

for i in 0..<scoresArray.count {
    for j in 0..<scoresArray[i].count {
        scoresArray[i][j] = Double(randomInt(50, max: 100))
    }
}

for (index, array) in scoresArray.enumerate() {
    var sum = 0.0
    for score in array {
        sum += score
    }
    let avg = sum / Double(coursesArray.count)
    print("\(namesArray[index])的平均成績(jī)?yōu)? \(avg)")
}

for i in 0..<coursesArray.count {
    var sum = 0.0
    for row in 0..<scoresArray.count {
        sum += scoresArray[row][i]
    }
    let avg = sum / Double(namesArray.count)
    print("\(coursesArray[i])課的平均成績(jī)?yōu)? \(avg)")
}

集合

集合在內(nèi)存中是離散的,集合中的元素通過計(jì)算Hash Code(哈希碼或散列碼)來決定存放在內(nèi)存中的什么位置,集合中不允許有重復(fù)元素

  • 創(chuàng)建集合
var a: Set<Int> = [1, 2, 1, 5, 8, 5]
  • 添加和刪除元素
a.insert(100)           // 添加元素

a.remove(2)             // 刪除元素
set.removeFirst()
set.removeAll()
  • 集合的運(yùn)算(交集、并集、差集)
var a: Set<Int> = [1, 2, 1, 2, 3, 4, 5]
var b: Set<Int> = [1, 3, 5, 7]

a.intersect(b)  // 交集(a和b都有的元素)
a.union(b)      // 并集(a和b的所有元素)
a.subtract(b)   // 差集(a有b沒有的元素)

字典

字典是以鍵值對(duì)的方式保存數(shù)據(jù)的容器,字典中的每個(gè)元素都是鍵值對(duì)的組合,通過鍵可以找到對(duì)應(yīng)的值。

  • 創(chuàng)建字典
var dict: [String: String] = [
    "abacus": "算盤",
    "abnormal": "異常的",
    "hello" : "你好",
    "good": "好的"
    ]

  • 添加、刪除、修改元素
// 添加元素
dict["shit"] = "狗屎"
dict["delicious"] = "好吃的"

// 刪除元素
// dict.removeValueForKey("hello")
dict["hello"] = nil
// 修改元素
dict["shit"] = "牛糞"
print(dict)
  • 遍歷元素

// 遍歷字典中所有的值
for value in dict.values {
    print(value)
}
// 遍歷字典中所有的鍵
for key in dict.keys {
    print("\(key) ---> \(dict[key])")
}
// 直接通過一個(gè)元組獲得字典中的鍵和值(原始類型)
for (key, value) in dict {
    print("\(key) ---> \(value)")
}

重要操作

  • 排序
  1. sort
  2. sortInPlace

說明:排序方法的參數(shù)是一個(gè)閉包(closeure),該閉包的作用是比較數(shù)組中兩個(gè)元素的大小

let array = [23, 45, 12, 89, 98, 55, 7]
array.sort ({ (one: Int, two: Int) -> Bool in
    return one < two
})
array.sort ({ (one, two) in one < two })
array.sort ({ one, two in one < two })
array.sort ({ $0 < $1 })
array.sort { $0 < $1 }
array.sort (<)

  • 過濾
let array = [23, 37, 96, 55, 40, 92, 68, 88]
//篩選不滿足條件的數(shù)據(jù)
let newArray1 = array.filter { $0 > 50 }
let newArray2 = array.filter { $0 % 2 == 0 }
print(newArray2)

  • 映射
let array = [23, 37, 96, 55, 40, 92, 68, 88]
//通過映射對(duì)數(shù)據(jù)進(jìn)行變換處理
let newArray3 = array.map { $0 * $0 }
let newArray4 = array.map { sqrt(Double($0)) }
  • 歸約
let result1 = array.reduce(0, combine: +)
print(result1)
let result2 = array.reduce(1, combine: *)
print(result2)
let result3 = array.reduce(array[0]) {
    $1 > $0 ? $1 : $0
}
print(result3)
let strArray = ["I", "love", "you"]
let result4 = strArray.reduce("") { $0 + " " + $1 }
print(result4)

函數(shù)和閉包

說明:函數(shù)是獨(dú)立的可重復(fù)使用的功能模塊,如果程序中出現(xiàn)了大量的重復(fù)代碼,通常都可以將這部分功能封裝成一個(gè)獨(dú)立的函數(shù)。在Swift中,函數(shù)是"一等公民",函數(shù)作為類型來使用,也就是說函數(shù)可以賦值給一個(gè)變量或常量,可以將函數(shù)作為函數(shù)的參數(shù)或者返回值,還可以使用高階函數(shù)。

func 函數(shù)名([參數(shù)1: 類型, 參數(shù)2: 類型, ...]) [throws|rethrows] [-> 返回類型]{
    函數(shù)的執(zhí)行體
    [return 表達(dá)式]
}

  • 外部參數(shù)名

說明:函數(shù)名(外部參數(shù)名 內(nèi)部參數(shù)名: 類型, 外部參數(shù)名 內(nèi)部參數(shù)名: 類型)
如果不寫外部參數(shù)名那么內(nèi)部參數(shù)名也是外部參數(shù)名
可以使用_來作為外部參數(shù)名表示省略外部參數(shù)名
調(diào)用函數(shù)的時(shí)候要寫函數(shù)的外部參數(shù)名

func myMin(a x: Int, b y: Int) -> Int {
    return x < y ? x : y
}
print(myMin(a: 3, b: 5))

  • inout參數(shù)

說明:inout - 輸入輸出參數(shù)(不僅將數(shù)據(jù)傳入函數(shù)還要從函數(shù)中取出數(shù)據(jù))
inout類型的參數(shù)前要加上&符號(hào)

func swap(inout a: Int, inout _ b: Int) -> Void {
    //(a, b) = (b, a)
    let temp = a
    a = b
    b = temp
}

var a = 300, b = 500
swap(&a, &b)  //inout類型的參數(shù)前要加上&符號(hào)
print("a = \(a)")
print("b = \(b)")
  • 可變參數(shù)列表
func sum(nums: Int...) -> Int {
    var total = 0
    for num in nums {
        total += num
    }
    return total
}

print(sum())
print(sum(999))
print(sum(1, 2, 3))
print(sum(90, 82, 37, 68, 55, 11, 99))

閉包就是沒有名字的函數(shù)(匿名函數(shù))或者稱之為函數(shù)表達(dá)式(Lambda表達(dá)式),Objective-C中與之對(duì)應(yīng)的概念叫block。如果一個(gè)函數(shù)的參數(shù)類型是函數(shù)我們可以傳入一個(gè)閉包;如果一個(gè)函數(shù)的返回類型是函數(shù)我們可以返回一個(gè)閉包;如果一個(gè)類的某個(gè)屬性是函數(shù)我們也可以將一個(gè)閉包表達(dá)式賦值給它。

{ ([參數(shù)列表]) [-> 返回類型] in 代碼 }

面向?qū)ο缶幊蹋∣OP)

基本概念

對(duì)象:接受消息的單元,對(duì)象是一個(gè)具體的概念

類:對(duì)象的藍(lán)圖和模板,類是一個(gè)抽象概念

消息:對(duì)象之間通信的手段,通過給對(duì)象發(fā)消息可以讓對(duì)象執(zhí)行對(duì)應(yīng)的操作來解決問題

四大支柱

抽象:定義類的過程就是一個(gè)抽象的過程,需要做數(shù)據(jù)抽象和行為抽象,數(shù)據(jù)抽象找到對(duì)象的屬性(保存對(duì)象狀態(tài)的存儲(chǔ)屬性),行為抽象找到對(duì)象的方法(可以給對(duì)象發(fā)的消息)

封裝:

  • 觀點(diǎn)1:我們?cè)陬愔袑懛椒ㄆ鋵?shí)就是在封裝API,方法內(nèi)部實(shí)現(xiàn)可能會(huì)很復(fù)雜,但是這些對(duì)調(diào)用者來說是不可見的,調(diào)用只能看到方法有一個(gè)簡(jiǎn)單清晰的接口。
  • 觀點(diǎn)2:將對(duì)象的屬性和操作這些屬性的方法綁定在一起。
  • 觀點(diǎn)3:隱藏一切可以隱藏的實(shí)現(xiàn)細(xì)節(jié),只提供簡(jiǎn)單清晰的接口(界面)。

繼承:

從已有的類創(chuàng)建新類的過程
提供繼承信息的稱為父類(超類/基類)
得到繼承信息的稱為子類(派生類/衍生類)
通常子類除了得到父類的繼承信息還會(huì)增加一些自己特有的東西
所以子類的能力一定比父類更強(qiáng)大
繼承的意義在于子類可以復(fù)用父類的代碼并且增強(qiáng)系統(tǒng)現(xiàn)有的功能

注意:
1.可以將子類型的對(duì)象賦值給父類型的變量(因?yàn)樽宇惛割愔g是IS-A關(guān)系)
例如:學(xué)生是人, 老師是人, 所以學(xué)生和老師的對(duì)象可以賦值給人類型的變量
2.如果要將父類型的變量轉(zhuǎn)換成子類型需要用as運(yùn)算符進(jìn)行類型轉(zhuǎn)換
  - 如果能夠確認(rèn)父類型的變量中就是某種子類型的對(duì)象可以用as!進(jìn)行轉(zhuǎn)換
  - 如果不確定父類型的變量中是哪種子類型可以用as?嘗試轉(zhuǎn)換(可以通過if+as?將
    父類型安全的轉(zhuǎn)換成子類型然后再調(diào)用子類特有方法)

多態(tài):

同樣的對(duì)象類型(Pet類型)接收相同的消息(調(diào)用相同的方法)
但是做了不同的事情 這就是多態(tài)(polymorphism)

  • 實(shí)現(xiàn)多態(tài)的關(guān)鍵步驟:
  1. 方法重寫(子類在繼承父類的過程中對(duì)父類已有的方法進(jìn)行重寫, 而且不同的子類給出各自不同的實(shí)現(xiàn)版本)
  2. 對(duì)象造型(將子類對(duì)象當(dāng)成父類型來使用)
  • 重要方式
    重載 - overload
    重寫 - override

三個(gè)步驟

  1. 定義類
    • 數(shù)據(jù)抽象
      • 存儲(chǔ)屬性
    • 行為抽象
      • 方法(寫到類里面的函數(shù)或者說跟對(duì)象綁定的行為就是方法)
        • 對(duì)象方法
        • 類方法(發(fā)給類的消息與對(duì)象狀態(tài)無關(guān))
    • 計(jì)算屬性
    • 構(gòu)造器
      • 指派構(gòu)造器
      • 便利構(gòu)造器
      • 必要構(gòu)造器
  2. 創(chuàng)建對(duì)象
  3. 給對(duì)象發(fā)消息

舉個(gè)例子。

class Triangle {
    var a: Double
    var b: Double
    var c: Double

    init(a: Double, b: Double, c: Double) {
        self.a = a
        self.b = b
        self.c = c
    }

    // 類方法(發(fā)給類的消息與對(duì)象狀態(tài)無關(guān))
    // 此處的static也可以換成class作用相同
    static func isValid(a: Double, _ b: Double, _ c: Double) -> Bool {
        return a + b > c && b + c > a && c + a > b
    }
    // 對(duì)象方法(發(fā)給對(duì)象的消息與對(duì)象狀態(tài)有關(guān))
    func perimeter() -> Double {
        return a + b + c
    }
}

let a = 1.0
let b = 2.0
let c = 3.0
// 在創(chuàng)建對(duì)象前先調(diào)用類方法判定給定的三條邊能否構(gòu)成三角形
// 類方法是發(fā)給類的消息所以不用創(chuàng)建對(duì)象直接通過類名調(diào)用
if Triangle.isValid(a, b, c) {
    let t = Triangle(a: a, b: b, c: c)
    // 對(duì)象方法是發(fā)給對(duì)象的消息要先創(chuàng)建對(duì)象才能調(diào)用
    print(t.perimeter())
}
else {
    print("無法創(chuàng)建三角形")
}

相關(guān)內(nèi)容

  • 枚舉(enum)
  • 結(jié)構(gòu)(體)(struct)

總結(jié): 類和結(jié)構(gòu)的區(qū)別到底有哪些?什么時(shí)候應(yīng)該使用結(jié)構(gòu)?什么時(shí)候應(yīng)該使用類?
區(qū)別1: 結(jié)構(gòu)的對(duì)象是值類型, 類的對(duì)象是引用類型
區(qū)別2: 結(jié)構(gòu)會(huì)自動(dòng)生成初始化方法
區(qū)別3: 結(jié)構(gòu)中的方法在默認(rèn)情況下是不允許修改結(jié)構(gòu)中的屬性除非加上mutating關(guān)鍵字
值類型在賦值的時(shí)候會(huì)在內(nèi)存中進(jìn)行對(duì)象的拷貝
引用類型在賦值的時(shí)候不會(huì)進(jìn)行對(duì)象拷貝只是增加了一個(gè)引用
結(jié)論: 我們自定義新類型時(shí)優(yōu)先考慮使用類而不是結(jié)構(gòu)除非我們要定義的是一種底層的數(shù)據(jù)結(jié)構(gòu)(保存其他數(shù)據(jù)的類型)

補(bǔ)充: 
    程序員可以使用的內(nèi)存大致分為五塊區(qū)域:
    棧 (stack) - 我們定義的局部變量/臨時(shí)變量都是放在棧上
    - 特點(diǎn): 小、快
    堆 (heap) - 我們創(chuàng)建的對(duì)象都是放在堆上的
    - 特點(diǎn): 大、慢
    靜態(tài)區(qū) (static area)
      - 數(shù)據(jù)段 - 全局量
      - 只讀數(shù)據(jù)段 - 常量
      - 代碼段 - 函數(shù)和方法
  • 擴(kuò)展(extension)

如果在某個(gè)特定的應(yīng)用場(chǎng)景中你發(fā)現(xiàn)現(xiàn)有的類缺少了某項(xiàng)功能那么可以通過類擴(kuò)展(extension)的方式現(xiàn)場(chǎng)添加這項(xiàng)功能

func randomInt(min: UInt32, _ max: UInt32) -> Int {
    return Int(arc4random_uniform(max - min + 1) + min)
}
//在UIColor類中添加一個(gè)產(chǎn)生隨機(jī)色的靜態(tài)方法
extension UIColor {
    static func randomColor() -> UIColor {
        let r = CGFloat(randomInt(0, 255)) / 255.0
        let g = CGFloat(randomInt(0, 255)) / 255.0
        let b = CGFloat(randomInt(0, 255)) / 255.0
        return UIColor(red: r, green: g, blue: b, alpha: 1)
    }
}
  • 運(yùn)算符重載
  • 下標(biāo)運(yùn)算(subscript)
    • 直接通過對(duì)象的下標(biāo)來對(duì)其進(jìn)行操作
  • 訪問修飾符
    • private
    • internal
    • public

綜合例子:求分?jǐn)?shù)的加減乘除運(yùn)算。

說明:該例子中包含了文章后面所涉及到的錯(cuò)誤處理的相關(guān)應(yīng)用,感覺放在例子中更通俗易懂。

Fraction.swift

import Foundation

// 短除法(歐幾里得算法)
// x和y的最大公約數(shù)跟y%x和x的最大公約數(shù)是一樣的
// Greatest Common Divisor
func gcd(x: Int, _ y: Int) -> Int {
    if x > y {
        return gcd(y, x)
    }
    else if y % x != 0 {
        return gcd(y % x, x)
    }
    else {
        return x
    }
}

// 定義一個(gè)遵循ErrorType協(xié)議的枚舉
// 通過不同的case定義程序中可能出現(xiàn)的若干種異常狀況
enum FractionError: ErrorType {
    case ZeroDenominator    // 分母為0
    case DivideByZero       // 除以0
}

class Fraction {
    //訪問修飾符
    private var _num: Int
    private var _den: Int
    
    var info: String {
        get {
            return _num == 0 || _den == 1 ? "\(_num)" : "\(_num)/\(_den)"
        }
    }
    
    // 如果一個(gè)方法拋出了異常 那么在聲明方法時(shí)必須要寫上throws關(guān)鍵字
    // throws關(guān)鍵字是提醒方法的調(diào)用者方法可能會(huì)出狀況 調(diào)用時(shí)要寫try
    init(num: Int, den: Int) throws {
        _num = num
        _den = den
        if _den == 0 {
            // 如果程序中出現(xiàn)問題就拋出錯(cuò)誤(異常)
            // 被throw關(guān)鍵字拋出的必須是遵循ErrorType協(xié)議的東西
            throw FractionError.ZeroDenominator
        }
        else {
            simplify()
            normalize()
        }
    }
    
    func add(other: Fraction) -> Fraction {
        // 如果能夠確保方法調(diào)用時(shí)不出異常那么可以在try關(guān)鍵字后加!
        // 這樣就可以在不寫do...catch的情況下調(diào)用可能出狀況的方法
        return try! Fraction(num: _num * other._den + other._num * _den, den: _den * other._den)
    }
    
    func sub(other: Fraction) -> Fraction {
        return try! Fraction(num: _num * other._den - other._num * _den, den: _den * other._den)
    }
    
    func mul(other: Fraction) -> Fraction {
        return try! Fraction(num: _num * other._num, den: _den * other._den)
    }
    
    func div(other: Fraction) throws -> Fraction {
        if other._num == 0 {
            throw FractionError.DivideByZero
        }
        return try! Fraction(num: _num * other._den, den: _den * other._num)
    }
    
    func normalize() -> Fraction {
        if _den < 0 {
            _num = -_num
            _den = -_den
        }
        return self
    }
    
    func simplify() -> Fraction {
        if _num == 0 {
            _den = 1
        }
        else {
            let x = abs(_num)
            let y = abs(_den)
            let g = gcd(x, y)
            _num /= g
            _den /= g
        }
        return self
    }
}

// 運(yùn)算符重載(為自定義的類型定義運(yùn)算符)

func +(one: Fraction, two: Fraction) -> Fraction {
    return one.add(two)
}

func -(one: Fraction, two: Fraction) -> Fraction {
    return one.sub(two)
}

func *(one: Fraction, two: Fraction) -> Fraction {
    return one.mul(two)
}

func /(one: Fraction, two: Fraction) throws -> Fraction {
    return try one.div(two)
}

main.swift

// 對(duì)于可能出狀況的代碼要放在do...catch中執(zhí)行
// 在可能出狀況的方法前還要寫上try表示嘗試著執(zhí)行
// 如果在do中沒有出現(xiàn)任何狀況那么catch就不會(huì)執(zhí)行
// 如果do中出現(xiàn)了狀況代碼就不會(huì)再向下繼續(xù)執(zhí)行而是轉(zhuǎn)移到catch中
// 在do的后面可以跟上多個(gè)catch用于捕獲不同的異常狀況 但是最多只有一個(gè)catch會(huì)被執(zhí)行
import Foundation
do {
    let f1 = try Fraction(num: 3, den: 4)
    let f2 = try Fraction(num: 0, den: 9)

    print(f1.info)
    print(f2.info)

    let f3 = f1 + f2
    print(f3.info)
    let f4 = f1 - f2
    print(f4.info)
    let f5 = f1 * f2
    print(f5.info)
    let f6 = try f1 / f2
    print(f6.info)
}
catch FractionError.ZeroDenominator {
    print("瓜西西的, 分母不能為0!!!")
}
catch FractionError.DivideByZero {
    print("卵球了, 除以0是不行的!!!")
}
catch {
    print("出錯(cuò)了! 我也不知道什么問題")
}

有關(guān)錯(cuò)誤處理的補(bǔ)充

func foo() {
    // 如果能夠保證代碼不出錯(cuò)可以在try后面加!
    // 如果不確定代碼是否出錯(cuò)可以在try后面加?
    // 需要注意的是有?的地方會(huì)產(chǎn)生Optional(可空類型)
    // 稍后可能還需要對(duì)可空類型進(jìn)行拆封, 拆封方式有二: 
    //  1. 不安全的做法: xxx!
    //  2. 安全的做法: 用if let = xxx { }進(jìn)行拆封
    let f1 = try? Fraction(num: 3, den: 0)
    let f2 = try? Fraction(num: 0, den: 9)
    
    if let a = f1, b = f2 {
        let f3 = a + b
        print(f3.info)
    }
    else {
        print("無效的分?jǐn)?shù)無法進(jìn)行加法運(yùn)算")
    }
}

foo()

面向協(xié)議編程(POP)

協(xié)議

protocol 協(xié)議名[: 父協(xié)議1, 父協(xié)議2, ...] {
    //方法的集合(計(jì)算屬性相當(dāng)于就是方法)
}

  1. 能力 - 遵循了協(xié)議就意味著具備了某種能力
  2. 約定 - 遵循了協(xié)議就一定要實(shí)現(xiàn)協(xié)議中的方法
  3. 角色 - 一個(gè)類可以遵循多個(gè)協(xié)議, 一個(gè)協(xié)議可以被多個(gè)類遵循, 遵循協(xié)議就意味著扮演了某種角色, 遵循多個(gè)協(xié)議就意味著可以扮演多種角色

Swift中的繼承是單一繼承(一個(gè)類只能有一個(gè)父類), 如果希望讓一個(gè)類具備多重能力可以使用協(xié)議來實(shí)現(xiàn)(C++里面是通過多重繼承來實(shí)現(xiàn)的, 這是一種非常不好的做法)

依賴倒轉(zhuǎn)原則(面向協(xié)議編程)

  1. 聲明變量的類型時(shí)應(yīng)該盡可能使用協(xié)議類型
  2. 聲明方法參數(shù)類型時(shí)應(yīng)該盡可能使用協(xié)議類型
  3. 聲明方法返回類型時(shí)應(yīng)該盡可能使用協(xié)議類型

用協(xié)議實(shí)現(xiàn)委托回調(diào)

一個(gè)對(duì)象想做某件事情但是自身沒有能力做這件事可以使用委托回調(diào)

  1. 設(shè)計(jì)協(xié)議,被委托方遵循協(xié)議實(shí)現(xiàn)方法
  2. 委托方有一個(gè)屬性是協(xié)議類型的,通過該屬性可以調(diào)用協(xié)議中的方法

注意: 委托方的協(xié)議類型的屬性通常是可空類型,因?yàn)橐獙懗蓋eak引用(委托回調(diào)和代理模式是有區(qū)別不可混淆

  • 委托回調(diào)
import Foundation
//委托回調(diào)
protocol ExamDelegate: class {
    
    func answerTheQuestion()
}

class LazyStudent {
    var name: String
    weak var delegate: ExamDelegate?
    
    init(name: String) {
        self.name = name
    }
    
    func joinExam() {
        print("姓名: \(name)")
        delegate?.answerTheQuestion()
    }
}

class Gunman: ExamDelegate {
    
    func answerTheQuestion() {
        print("奮筆疾書各種答案")
    }
}

let stu = LazyStudent(name: "王大錘")
let gun = Gunman()
stu.delegate = gun
stu.joinExam()
  • 代理模式
import Foundation
protocol ExamCandidate: class {
    
    func answerTheQuestion()
}

class LazyStudent: ExamCandidate {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func answerTheQuestion() {
    }
}

class Gunman: ExamCandidate {
    var name: String
    var target: LazyStudent?
    
    init(name: String) {
        self.name = name
    }
    
    func answerTheQuestion() {
        if let stu = target {
            print("姓名: \(stu.name)")
            print("奮筆疾書答案")
            print("提交試卷")
        }
    }
}

let stu = LazyStudent(name: "王大錘")
let gun = Gunman(name: "駱昊")
gun.target = stu
gun.answerTheQuestion()

其他

  • 協(xié)議組合: protocol<協(xié)議1,協(xié)議2,...>
  • 可選方案
  • 協(xié)議擴(kuò)展

下面是一個(gè)關(guān)于協(xié)議的例子。

Book.swift

/// 圖書
class Book {
    var name: String
    var price: Double
    var type: String
    
    // 四人幫設(shè)計(jì)模式 - 策略模式
    var strategy: DiscountStrategy?
    
    /**
     初始化方法
     - parameter name:  書名
     - parameter price: 價(jià)格
     - parameter type:  類型
     */
    init(name: String, price: Double, type: String) {
        self.name = name
        self.price = price
        self.type = type
    }
    
    /// 減多少錢
    var discountValue: Double {
        get {
            if let s = strategy {
                return s.discount(price)
            }
            else {
                return 0
            }
        }
    }
    
    /// 折后價(jià)格
    var discountedPrice: Double {
        get { return price - discountValue }
    }
}

DiscountStrategy.swift

/**
 *  打折策略協(xié)議
 */
protocol DiscountStrategy {
    
    /**
     計(jì)算折扣
     - parameter price: 原價(jià)
     - returns: 折扣的金額
     */
    func discount(price: Double) -> Double
}

/// 百分比折扣策略
class PercentageDiscount: DiscountStrategy {
    var percentage: Double
    
    init(percentage: Double) {
        self.percentage = percentage
    }
    
    func discount(price: Double) -> Double {
        return price * (1 - percentage)
    }
}

// 固定金額折扣策略
class FixedDiscount: DiscountStrategy {
    var fixedMoney: Double
    
    init(fixedMoney: Double) {
        self.fixedMoney = fixedMoney
    }
    
    func discount(price: Double) -> Double {
        return price >= fixedMoney ? fixedMoney : 0
    }
}

// 分段折后策略
class SegmentedDiscount: DiscountStrategy {
    
    func discount(price: Double) -> Double {
        if price < 20 {
            return 0
        }
        else if price < 50 {
            return 3
        }
        else if price < 100 {
            return 10
        }
        else {
            return 30
        }
    }
}

main.swift

let booksArray = [
    Book(name: "C語言程序設(shè)計(jì)", price: 24.0, type: "計(jì)算機(jī)"),
    Book(name: "名偵探柯南", price: 98.5, type: "漫畫"),
    Book(name: "Swift從入門到住院", price: 35.8, type: "計(jì)算機(jī)"),
    Book(name: "黃岡數(shù)學(xué)密卷", price: 34.2, type: "教材"),
    Book(name: "中國股市探秘", price: 58.5, type: "金融")
]

let discountDict: [String: DiscountStrategy] = [
    "計(jì)算機(jī)": PercentageDiscount(percentage: 0.78),
    "教材": PercentageDiscount(percentage: 0.85),
    "漫畫": SegmentedDiscount(),
    "科普": FixedDiscount(fixedMoney: 2)
]

var totalPrice = 0.0
var totalDiscount = 0.0
for book in booksArray {
    if let strategy = discountDict[book.type] {
        book.strategy = strategy
    }
    print("《\(book.name)》原價(jià): ¥\(book.price)元")
    print("《\(book.name)》折后價(jià): ¥\(book.discountedPrice)元")
    totalPrice += book.discountedPrice
    totalDiscount += book.discountValue
}

print(String(format: "總計(jì): ¥%.1f元", totalPrice))
print(String(format: "為您節(jié)省了: ¥%.1f元", totalDiscount))

運(yùn)行截圖:

屏幕快照 2016-08-16 下午8.22.43.png

泛型

讓類型不在是程序中的硬代碼(hard code),可以設(shè)計(jì)出更通用的代碼。

  • 泛型函數(shù)
// 定義一個(gè)虛擬類型T, 調(diào)用函數(shù)時(shí)根據(jù)傳入的參數(shù)類型來決定T到底是什么
func mySwap<T>(inout a: T, inout _ b: T) {
    let temp = a
    a = b
    b = temp
}
  • 泛型類、結(jié)構(gòu)、枚舉
struct Stack<T> {
    var data: [T] = []
    
    // 入棧
    mutating func push(elem: T) {
        data.append(elem)
    }
    
    // 出棧
    mutating func pop() -> T {
        return data.removeLast()
    }
    
    var isEmpty: Bool {
        get { return data.count == 0 }
    }
}

var stack = Stack<String>()
stack.push("hello")
stack.push("good")
stack.push("zoo")

while !stack.isEmpty {
    print(stack.pop())
}

相關(guān)知識(shí)

  • 泛型限定

<T: Comparable>限定T類型必須是遵循了Comparable協(xié)議的類型

  • where子句

錯(cuò)誤處理

提示:請(qǐng)看上述求分?jǐn)?shù)的加減乘除運(yùn)算的例子
目標(biāo):只要明白以下關(guān)鍵字的用法即可

  • throw
  • throw / rethrows
  • do
  • catch
  • try

邊角知識(shí)

  • ARC - 自動(dòng)引用計(jì)數(shù)
  • 正則表達(dá)式
  • 嵌套類型

--更多精彩內(nèi)容請(qǐng)關(guān)注:Youth丶夏夏--

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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