學(xué)習(xí)Swift一個(gè)月總結(jié)

個(gè)人感悟:一個(gè)月的學(xué)習(xí),學(xué)了很多內(nèi)容,但是并沒(méi)能很好的消化,所以說(shuō)現(xiàn)在腦子里還是一片混亂,不能很好的將學(xué)習(xí)過(guò)的東西應(yīng)用在編寫(xiě)程序上,所以說(shuō)必須要一遍遍的去敲代碼練習(xí),fighting!

語(yǔ)言基礎(chǔ)

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

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

變量和常量

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

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

let d: Int = 10
// d = 100    // compiler error 編譯時(shí)出錯(cuò)
let e = 1000

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

語(yǔ)言元素

var a: Int = 10

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

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

運(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_234_567、0x10、0o10、0b10、
2.小數(shù)字面量:123.45、1.2345e2、0xab.cdp2
3.字符字面量:"c"、"\n"、"\u{41}"、"\u{9a86}、"\t"
4.字符串字面量:"Hello"、"caf\u{e9}"
5.布爾字面量:true、false
6.空值字面量:nil
7.類(lèi)型字面量:String.self、UILabel.self

分隔符:將不同的語(yǔ)言元素符號(hào)分開(kāi)

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

分支和循環(huán)

分支

  • if...else...
// 分段函數(shù)
let x = 3.2
let y: Double
if x < -1 {
   y = 3 * x + 5
}
else if x <= 1 {
   y = 5 * x - 3
}
else {
   y = 7 * x + 1
}
print("f(\(x))=\(y)")

  • switch...case...default
// IT從業(yè)人員等級(jí)評(píng)定
print("請(qǐng)輸入你的月薪: ", terminator: "")
let salary = inputInt()
if salary >= 0 {
    switch salary {
    case 0:
        print("你是一個(gè)無(wú)業(yè)游民")
    case 1...30000:
        print("你是一個(gè)小碼畜")
    case 30001...60000:
        print("你是一個(gè)小碼奴")
    case 60001...100000:
        print("你是一個(gè)大碼農(nóng)")
    case 100001...200000:
        print("你是一個(gè)光榮的IT民工")
    case 200001...500000:
        print("你是一個(gè)優(yōu)秀的IT工程師")
    case 500001..<10000000:
        print("你是IT精英")
    case 10000000..<100000000:
        print("你是IT大哥, 我服")
    default:
        print("你是頭上有光環(huán)的IT領(lǐng)袖, 求帶")
    }
}
else {
    print("你丫有病!!!")
}

循環(huán)

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

// 求和1-100

var sum = 0
for i in 1...100 {
        sum += i
}
print(sum)
// 冒泡排序:兩兩比較,前面的元素比后面的元素大就交換位置

var array = [98, 29, 35, 12, 47, 66, 53, 79]

for i in 0..<array.count - 1 {
    var swapped = false
    for j in 0..<array.count - 1 - i {
        if array[j] > array[j + 1] {
            (array[j], array[j + 1]) = (array[j + 1], array[j])
        }
    }
    if !swapped {
        break
    }
}

print(array)

// 簡(jiǎn)單選擇排序:每次從剩下的元素中找最小的元素放到對(duì)應(yīng)位置

var array = [98, 29, 35, 12, 47, 66, 53, 79]
for i in 0..<array.count - 1 {//比較次數(shù)
    var minIndex = i
    for j in i + 1..<array.count {
        if array[j] < array[minIndex] {
            minIndex = j
        }
    }
    (array[i], array[minIndex]) = (array[minIndex], array[i])
}

print(array)

//1-10000之間所有的完美數(shù)
func cube(n: Int) -> Int {
    return n * n * n
}
for i in 100...999 {
    let a = i % 10          // 個(gè)位
    let b = i / 10 % 10     // 十位
    let c = i / 100         // 百位
    if i == cube(a) + cube(b) + cube(c) {
        print(i)
    }
}

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

下面的程序?qū)崿F(xiàn)了"百錢(qián)百雞"

for x in 0...20 {
    for y in 0...33 {
        let z = 100 - x - y
        if 5 * x + y * 3 + z / 3 == 100 && z % 3 == 0 {
        print("公雞:\(x), 母雞:\(y), 小雞:\(z)")
        }
    }
}

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

綜合案例:Craps賭博游戲

游戲規(guī)則:玩家搖色子,如果第一次搖出了7點(diǎn)或11點(diǎn),玩家勝;如果搖出了2點(diǎn)、3或12點(diǎn),莊家勝;其他點(diǎn)數(shù)游戲繼續(xù)。再繼續(xù)的過(guò)程中玩家重新?lián)u色子,如果搖出了7點(diǎn),莊家勝;如果搖出了第一次搖的點(diǎn)數(shù),玩家勝;否則玩家繼續(xù)搖色子直到分出勝負(fù)。

func roll () -> Int {
    return Int(arc4random_uniform(6)) + 1
}
var money = 1000
var needsGoOn = false
repeat{
    print("資產(chǎn):\(money)元")
    var debt: Int
    repeat{
        print("請(qǐng)下注:", terminator: "")
        debt = inputInt()
        } while debt <= 0 || debt > money
    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
    }
    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("你boom了")

容器

數(shù)組

數(shù)組是使用連續(xù)的內(nèi)存空間保存多個(gè)同類(lèi)型的元素的容器,因?yàn)閿?shù)組中的元素在內(nèi)存中是連續(xù)的,所以可以使用下標(biāo)

  • 創(chuàng)建數(shù)組
var array1: [Int] = []
var array2: Array<Int> = []
var array3 = [1, 2, 3, 4, 5]
var array4 = [Int](count: 5, repeatedValue: 0)
var array5 = Array<Int>(count: 5, repeatedValue: 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()
  • 修改元素
array3[0] = 100
array3[array3.count - 1] = 500
  • 遍歷數(shù)組
    1.方式1:
for i in 0..<array3.count {
    print(array3[i])
}

2.方式2:

for temp in array3 {
    print(temp)
}
for temp in array3[1...3] {
    print(temp)
}

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

3.方式3:

for (i, temp) in array3.enumerate() {
    if i == 0 {
        array3[i] = 1
    }
    print("\(i). \(temp)")
}

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

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

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

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

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

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)存中是離散的,集合中的元素通過(guò)計(jì)算Hash Code(哈希碼或散列碼)來(lái)決定存放在內(nèi)存中的什么位置,集合中不允許有重復(fù)元素。

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

set.remove(5)
set.removeFirst()
set.removeAll()
  • 集合運(yùn)算(交集、并集、差集)
var set1: Set<Int> = [1, 2, 1, 2, 3, 4, 5]
var set2: Set<Int> = [1, 3, 5, 7]

set1.intersect(set2)
set1.union(set2)
set1.subtract(set2)
  • 交集(a和b都有的元素):
print(a.intersect(b)) // 輸出為:[5, 3]
  • 并集(a和b的所有元素):
print(a.union(b)) // 輸出為:[2, 9, 5, 7, 3, 1, 11]
  • 差集(a有b沒(méi)有的元素):
print(a.subtract(b)) // 輸出為:[2, 1]

字典

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

  • 創(chuàng)建字典
let dict: [Int: String] = [
    1:"hello"
    2:"good"
    3:"wonderful"
    5:"delicious"
]
  • 添加、刪除、修改元素
dict[3] = "terrible"
dict[4] = "shit"
dict[5] = nil
  • 遍歷元素
for key in dict.keys {
    print("\(key) ---> \(dict[key])")
}
for value in dict.values {
    print(value)
}
for (key, value) in dict {
    print("\(key) ---> \(value)")
}

重要操作

  • 排序
    1.sort
    2.sortInPlace

說(shuō)明:排序方法的參數(shù)是一個(gè)閉包(closure),該閉包的作用是比較數(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(<)
  • 過(guò)濾
let array = [23, 45, 12, 89, 98, 55, 7]
// 篩選掉不滿(mǎn)足條件的數(shù)據(jù)
let newArray1 = array.filter {$0 > 50}
print(newArray) // [89, 98, 55]
  • 映射
let array = [23, 45, 12, 89, 98, 55, 7]
// 通過(guò)映射對(duì)數(shù)據(jù)進(jìn)行變換處理
let newArray = array.map { $0 % 10 }
print(newArray)

  • 歸約
let array = [23, 45, 12, 89, 98, 55, 7]
let result = array.reduce(0, combine: +)
print(result)

函數(shù)和閉包

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

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

  • 外部參數(shù)名

func myMin(a x: Int, b y: Int) -> Int {
    return x < y ? x : y
  • inout參數(shù)
    ~inout - 輸入輸出參數(shù)(不僅將數(shù)據(jù)傳入函數(shù)還要從函數(shù)中取出數(shù)據(jù))
    ~inout類(lèi)型的參數(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)
print("a = \(a)")      // 輸出為:a = 500
print("b = \(b)")      // 輸出為:b = 300
func createX(inout x: Int) {
    x = 1000
}
var x = 1
createX(&x)
print(x)       // 輸出為: 1000
  • 可變參數(shù)列表
func sum(nums: Int...) -> Int {
    var total = 0
    for num in nums {
        total += num
    }
    return total
}

print(sum())                           // 輸出為: 0
print(sum(999))                        // 輸出為:999
print(sum(1, 2, 3))                    // 輸出為:6
print(sum(90, 82, 37, 68, 55, 11, 99)) // 輸出為:442

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

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

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

基本概念

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

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

消息:對(duì)象之間通信的方式,通過(guò)給對(duì)象發(fā)消息可以讓對(duì)象執(zhí)行對(duì)應(yīng)的操作來(lái)解決問(wèn)題。

四大支柱

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

封裝:

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

繼承:從已有的類(lèi)創(chuàng)建新類(lèi)的過(guò)程稱(chēng)為繼承

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

  • 實(shí)現(xiàn)多態(tài)的關(guān)鍵步驟:
    ①方法重寫(xiě)(子類(lèi)在繼承父類(lèi)的過(guò)程中對(duì)父類(lèi)已有的方法進(jìn)行重寫(xiě), 而且不同的子類(lèi)給出各自不同的實(shí)現(xiàn)版本)
    ②對(duì)象造型(將子類(lèi)對(duì)象當(dāng)成父類(lèi)型來(lái)使用)
    ~可以通過(guò)if+as?將父類(lèi)型安全的轉(zhuǎn)換成子類(lèi)型然后再調(diào)用子類(lèi)特有方法

三個(gè)步驟

  1. 定義類(lèi)
  • 數(shù)據(jù)抽象
    • 存儲(chǔ)屬性
  • 行為抽象
    • 方法(寫(xiě)到類(lèi)里面的函數(shù)就是方法或者說(shuō)跟對(duì)象綁定的行為)
      • 對(duì)象方法:給對(duì)象法的消息要先創(chuàng)建對(duì)象才能調(diào)用,與對(duì)象狀態(tài)有關(guān)
      • 類(lèi)方法:給類(lèi)法的消息所以不用創(chuàng)建對(duì)象直接通過(guò)類(lèi)名調(diào)用,與對(duì)象的狀態(tài)無(wú)關(guān)的方法 方法前加 static或class,作用相同
  • 構(gòu)造器
    • 指派構(gòu)造器
    • 便利構(gòu)造器(convenience)
    • 必要構(gòu)造器(required)
  1. 創(chuàng)建對(duì)象
  2. 給對(duì)象發(fā)消息
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
    }

    // 類(lèi)方法(發(fā)給類(lèi)的消息與對(duì)象狀態(tài)無(wú)關(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)用類(lèi)方法判定給定的三條邊能否構(gòu)成三角形
// 類(lèi)方法是發(fā)給類(lèi)的消息所以不用創(chuàng)建對(duì)象直接通過(guò)類(lèi)名調(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("無(wú)法創(chuàng)建三角形")
}

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

  • 枚舉

~枚舉是定義符號(hào)常量的最佳方式
~符號(hào)常量總是優(yōu)于字面常量
~示例(枚舉和文檔注釋的應(yīng)用):

/**
 花色的枚舉
 
 - Spade:   黑桃
 - Heart:   紅心
 - Club:    草花
 - Diamond: 方塊
 */
enum Suite: String {
    case Spade = "??"
    case Heart = "??"
    case Club = "??"
    case Diamond = "??"
}

/// 一張牌
class Card {
    var suite: Suite
    var face: Int
    
    /**
     初始化方法
     - parameter suite: 花色
     - parameter face:  點(diǎn)數(shù)
     */
    init(suite: Suite, face: Int) {
        self.suite = suite
        self.face = face
    }
    
    /// 牌的信息
    var info: String {
        get {
            var str = suite.rawValue
            switch face {
            case 1: str += "A"
            case 11: str += "J"
            case 12: str += "Q"
            case 13: str += "K"
            default: str += "\(face)"
            }
            return str
        }
    }
}

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

總結(jié):類(lèi)和結(jié)構(gòu)的區(qū)別到底有哪些?什么時(shí)候應(yīng)該使用結(jié)構(gòu)?什么時(shí)候應(yīng)該使用類(lèi)?

  • 擴(kuò)展(extension)
    ~如果在某個(gè)特定的應(yīng)用場(chǎng)景中你發(fā)現(xiàn)現(xiàn)有的類(lèi)缺少了某項(xiàng)功能
    那么可以通過(guò)類(lèi)擴(kuò)展(extension)的方式現(xiàn)場(chǎng)添加這項(xiàng)功能
  • 運(yùn)算符重載 (要寫(xiě)在類(lèi)的外面)(為自定義的類(lèi)型定義運(yùn)算符)
class Fraction {
    private var _num: Int
    private var _den: Int
    
    var info: String {
        get {
            return _num == 0 || _den == 1 ? "\(_num)" : "\(_num)/\(_den)"
        }
    }
    
    init(num: Int, den: Int) {
        _num = num
        _den = den
        simplify()
        normalize()
    }
    
    func add(other: Fraction) -> Fraction {
        return Fraction(num: _num * other._den + other._num * _den, den: _den * other._den)
    }
    
    func sub(other: Fraction) -> Fraction {
        return Fraction(num: _num * other._den - other._num * _den, den: _den * other._den)
    }
    
    func mul(other: Fraction) -> Fraction {
        return Fraction(num: _num * other._num, den: _den * other._den)
    }
    
    func div(other: Fraction) -> Fraction {
        return 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)算符重載(為自定義的類(lèi)型定義運(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) -> Fraction {
    return one.div(two)
}
  • 下標(biāo)運(yùn)算(subscript)

  • 訪(fǎng)問(wèn)修飾符

    • private
    • internal
    • public

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

協(xié)議

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

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

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

依賴(lài)倒轉(zhuǎn)原則

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

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

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

一個(gè)對(duì)象想做某件事但是自身沒(méi)有能力做這件事,就可以使用委托回調(diào),具體步驟是:

  1. 設(shè)計(jì)一個(gè)協(xié)議,讓被委托方遵循協(xié)議并實(shí)現(xiàn)協(xié)議中的方法
  2. 委托方有一個(gè)屬性是協(xié)議類(lèi)型的,通過(guò)該屬性可以調(diào)用協(xié)議中的方法
    示例:五子棋App
import UIKitA

// RenjuBoard.swift

/**
 棋盤(pán)交叉點(diǎn)的狀態(tài)
 
 - Space: 空格
 - Black: 黑棋
 - White: 白棋
 */
enum PointState {
    case Space, Black, White
}

/// 棋盤(pán)
class RenjuBoard {
    var board: [[PointState]]
    var isBlackTurn = true
    var isGameOver = false
    
    init() {
        board = [[PointState]](count: 15, repeatedValue: [PointState](count: 15, repeatedValue: .Space))
    }
    
    // 索引器語(yǔ)法 - 可以直接對(duì)棋盤(pán)對(duì)象做下標(biāo)運(yùn)算來(lái)放置棋子
    subscript(row: Int, col: Int) -> Bool {
        get { return board[row][col] == .Space }
        set(isBlack) {
            if board[row][col] == .Space {
                board[row][col] = isBlack ? .Black : .White
                isBlackTurn = !isBlackTurn
            }
        }
    }
    
    func reset() {
        isGameOver = false
        isBlackTurn = true
        for i in 0..<board.count {
            for j in 0..<board[i].count {
                board[i][j] = .Space
            }
        }
    }
    
    func judge(row: Int, _ col: Int) -> Bool {
        return _judgeH(row, col) || _judgeV(row, col) || _judgeX1(row, col) || _judgeX2(row, col)
    }

    private func _judgeH(row: Int, _ col: Int) -> Bool {
        var counter = 1
        var currentCol = col - 1
        while currentCol >= 0 {
            if board[row][currentCol] == board[row][col] {
                counter += 1
                currentCol -= 1
            }
            else {
                break
            }
        }
        currentCol = col + 1
        while currentCol < board.count {
            if board[row][currentCol] == board[row][col] {
                counter += 1
                currentCol += 1
            }
            else {
                break
            }
        }
        return counter >= 5
    }
    
    private func _judgeV(row: Int, _ col: Int) -> Bool {
        var counter = 1
        var currentRow = row - 1
        while currentRow >= 0 {
            if board[currentRow][col] == board[row][col] {
                counter += 1
                currentRow -= 1
            }
            else {
                break
            }
        }
        currentRow = row + 1
        while currentRow < board.count {
            if board[currentRow][col] == board[row][col] {
                counter += 1
                currentRow += 1
            }
            else {
                break
            }
        }
        return counter >= 5
    }
    
    private func _judgeX1(row: Int, _ col: Int) -> Bool {
        var counter = 1
        var currentRow = row - 1
        var currentCol = col - 1
        while currentRow >= 0 && currentCol > 0 {
            if board[currentRow][currentCol] == board[row][col] {
                counter += 1
                currentRow -= 1
                currentCol -= 1
            }
            else {
                break
            }
        }
        currentRow = row + 1
        currentCol = col + 1
        while currentRow < board.count && currentCol < board.count {
            if board[currentRow][currentCol] == board[row][col] {
                counter += 1
                currentRow += 1
                currentCol += 1
            }
            else {
                break
            }
        }
        return counter >= 5
    }
    
    private func _judgeX2(row: Int, _ col: Int) -> Bool {
        var counter = 1
        var currentRow = row - 1
        var currentCol = col + 1
        while currentRow >= 0 && currentCol < board.count {
            if board[currentRow][currentCol] == board[row][col] {
                counter += 1
                currentRow -= 1
                currentCol += 1
            }
            else {
                break
            }
        }
        currentRow = row + 1
        currentCol = col - 1
        while currentRow < board.count && currentCol >= 0 {
            if board[currentRow][currentCol] == board[row][col] {
                counter += 1
                currentRow += 1
                currentCol -= 1
            }
            else {
                break
            }
        }
        return counter >= 5
    }
    
    func draw() {
        let lineBP = UIBezierPath()
        
        // 繪制15條橫線(xiàn)和15條豎線(xiàn)來(lái)構(gòu)造一個(gè)棋盤(pán)
        for i in 0..<board.count {
            lineBP.moveToPoint(CGPointMake(10, 10 + 50 * CGFloat(i)))
            lineBP.addLineToPoint(CGPointMake(710, 10 + 50 * CGFloat(i)))
            lineBP.moveToPoint(CGPointMake(10 + 50 * CGFloat(i), 10))
            lineBP.addLineToPoint(CGPointMake(10 + 50 * CGFloat(i), 710))
        }
        lineBP.stroke()
        
        // 繪制棋盤(pán)的邊框
        let rectBP = UIBezierPath(rect: CGRectMake(3, 3, 714, 714))
        rectBP.lineWidth = 6
        rectBP.stroke()
        
        // 繪制天元和星
        let starsRectArray = [
            CGRectMake(155, 155, 10, 10),
            CGRectMake(555, 155, 10, 10),
            CGRectMake(155, 555, 10, 10),
            CGRectMake(555, 555, 10, 10),
            CGRectMake(355, 355, 10, 10)
        ]
        for starRect in starsRectArray {
            let ovalBP = UIBezierPath(ovalInRect: starRect)
            ovalBP.fill()
        }
        
        // 繪制棋盤(pán)上的棋子
        for i in 0..<board.count {
            for j in 0..<board[i].count {
                if board[i][j] != .Space {
                    let ovalBP = UIBezierPath(ovalInRect: CGRectMake(-10 + CGFloat(j) * 50, -10 + CGFloat(i) * 50, 40, 40))
                    (board[i][j] == .Black ? UIColor.blackColor() : UIColor.whiteColor()).set()
                    ovalBP.fill()
                }
            }
        }
    }
}

// Canvas.swift

// 有的時(shí)候某個(gè)對(duì)象要做某件事情但其自身又沒(méi)有能力做這件事情
// 這個(gè)時(shí)候就可以使用委托回調(diào)的編程模式讓別的對(duì)象來(lái)做這件事情
// 實(shí)現(xiàn)委托回調(diào)的編程模式有以下幾個(gè)步驟:
//  1. 設(shè)計(jì)一個(gè)協(xié)議(被委托方必須要遵循協(xié)議才能給別的對(duì)象當(dāng)委托)
protocol CanvasDelegate: class {
    
    // 協(xié)議里面的方法就是要委托其他對(duì)象做的事情
    func showMessage(canvas: Canvas, message: String)
}

class Canvas: UIView {
    // 2. 委托方添加一個(gè)屬性其類(lèi)型是遵循了協(xié)議的被委托方
    weak var delegate: CanvasDelegate?
    
    var renjuBoard = RenjuBoard()
    var isAutoMode = false
    
    func clearBoard() {
        renjuBoard.reset()
        setNeedsDisplay()
    }
    
    func randomMove() {
        let row = Int(arc4random_uniform(15))
        let col = Int(arc4random_uniform(15))
        if renjuBoard[row, col] {
            renjuBoard[row, col] = renjuBoard.isBlackTurn
            setNeedsDisplay()
        }
    }
    
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        // Swift 2中的guard大法, Swift 3中據(jù)說(shuō)要廢掉
        guard !isAutoMode else { return }
        // guard !renjuBoard.isGameOver else { return }
        
        if !renjuBoard.isGameOver {
            if let touch = touches.first {
                let point = touch.locationInView(self)
                let row = lround(Double(point.y - 10) / 50)
                let col = lround(Double(point.x - 10) / 50)
                if renjuBoard[row, col] {
                    renjuBoard[row, col] = renjuBoard.isBlackTurn
                    setNeedsDisplay()
                    if renjuBoard.judge(row, col) {
                        renjuBoard.isGameOver = true
                        // 3. 自己做不了的事情委托給別的對(duì)象來(lái)做
                        delegate?.showMessage(self, message: renjuBoard.isBlackTurn ? "白棋勝" : "黑棋勝")
                    }
                }
            }
        }
    }
    
    override func drawRect(rect: CGRect) {
        renjuBoard.draw()
    }

}

// ViewController.swift

//  4. 讓視圖控制器遵循協(xié)議成為被委托方(協(xié)議表能力)
class ViewController: UIViewController, CanvasDelegate {
    
    var timer: NSTimer?
    var canvas: Canvas!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        canvas = Canvas(frame: CGRectMake(0, 0, 720, 720))
        // canvas.isAutoMode = true
        //  6. 給畫(huà)布對(duì)象綁定委托(self就是視圖控制器對(duì)象它遵循了協(xié)議所以有充當(dāng)委托的能力也就是說(shuō)可以扮演被委托方的角色)
        canvas.delegate = self
        canvas.center = self.view.center
        canvas.backgroundColor = UIColor(red: 254.0 / 255.0, green: 209.0 / 255.0, blue: 46.0 / 255.0, alpha: 1)
        self.view.addSubview(canvas)
        
        // timer = NSTimer.scheduledTimerWithTimeInterval(0.2, target: canvas, selector: "randomMove", userInfo: nil, repeats: true)
    }
    
    //  5. 遵循協(xié)議就必須要實(shí)現(xiàn)協(xié)議中的方法(協(xié)議表約定)
    func showMessage(canvas: Canvas, message: String) {
        let alertController = UIAlertController(title: message, message: "", preferredStyle: .Alert)
        let okAction = UIAlertAction(title: "確定", style: .Default) { action in
            // 此處通過(guò)尾隨閉包來(lái)定義點(diǎn)擊確定按鈕后要做什么
            canvas.clearBoard()
        }
        alertController.addAction(okAction)
        self.presentViewController(alertController, animated: true, completion: nil)
    }
    
    // deinit在銷(xiāo)毀對(duì)象的時(shí)候調(diào)用
    deinit {
        // 銷(xiāo)毀計(jì)時(shí)器
        timer?.invalidate()
    }
}

注意:委托方的協(xié)議類(lèi)型的屬性通常是可空類(lèi)型,因?yàn)橐獙?xiě)成弱引用(weak)。

其他

  • 協(xié)議組合: protocol<協(xié)議1, 協(xié)議2, ...>
  • 可選方法
  • 協(xié)議擴(kuò)展:對(duì)協(xié)議中的方法給出默認(rèn)實(shí)現(xiàn),也就是說(shuō)如果某個(gè)類(lèi)遵循了協(xié)議但是沒(méi)有實(shí)現(xiàn)這個(gè)方法就直接使用默認(rèn)實(shí)現(xiàn),那么這個(gè)方法也就相當(dāng)于是一個(gè)可選方法(可以實(shí)現(xiàn)也可以不實(shí)現(xiàn))
  • 協(xié)議中全是抽象概念(只有聲明沒(méi)有實(shí)現(xiàn)) 遵循協(xié)議的類(lèi)可以各自對(duì)協(xié)議中的計(jì)算屬性和方法給出自己的實(shí)現(xiàn)版本 這樣當(dāng)我們面向協(xié)議編程時(shí)就可以把多態(tài)的優(yōu)勢(shì)發(fā)揮到淋漓盡致 可以寫(xiě)出更通用更靈活的代碼(符合開(kāi)閉原則)
  • 實(shí)現(xiàn)開(kāi)閉原則最關(guān)鍵有兩點(diǎn):
  1. 抽象是關(guān)鍵(在設(shè)計(jì)系統(tǒng)的時(shí)候一定要設(shè)計(jì)好的協(xié)議);
  2. 封裝可變性(橋梁模式 - 將不同的可變因素封裝到不同的繼承結(jié)構(gòu)中)
  • 接口(協(xié)議)隔離原則: 協(xié)議的設(shè)計(jì)要小而專(zhuān)不要大而全,協(xié)議的設(shè)計(jì)也要高度內(nèi)聚
  • 示例:打折策略協(xié)議
import Foundation

/**
 *  打折策略協(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
        }
    }
}

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

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

let discountDict: [String: DiscountStrategy] = [
    "計(jì)算機(jī)": PercentageDiscount(percentage: 0.78),
    "教材": PercentageDiscount(percentage: 0.85),
    "漫畫(huà)": 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))

泛型

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

  • 泛型函數(shù)
    通過(guò)示例來(lái)理解泛型函數(shù)
import Foundation

// 泛型 (generic) - 讓類(lèi)型不再是程序中的硬代碼(寫(xiě)死的東西)

func bubbleSort<T: Comparable>(array: [T]) -> [T] {
    var newArray = array
    for i in 0..<newArray.count - 1 {
        var swapped = false
        for j in 0..<newArray.count - 1 - i {
            if newArray[j] > newArray[j + 1] {
                mySwap(&newArray[j], &newArray[j + 1])
                swapped = true
            }
        }
        if !swapped {
            break
        }
    }
    return newArray
}

// 定義一個(gè)虛擬類(lèi)型T, 調(diào)用函數(shù)時(shí)根據(jù)傳入的參數(shù)類(lèi)型來(lái)決定T到底是什么
func mySwap<T>(inout a: T, inout _ b: T) {
    let temp = a
    a = b
    b = temp
}

// 泛型限定
// <T: Comparable>限定T類(lèi)型必須是遵循了Comparable協(xié)議的類(lèi)型
func myMin<T: Comparable>(a: T, _ b: T) -> T {
    return a < b ? a : b
}


let array1: Array<Int> = [23, 45, 99, 12, 68, 51, 70, 66]
let array2 = bubbleSort(array1)
print(array1)
print(array2)

let array3 = ["hello", "zoo", "kiss", "apple", "good"]
let array4 = bubbleSort(array3)
print(array3)
print(array4)

class Student: Comparable {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

func ==(one: Student, two: Student) -> Bool {
    return one.name == two.name
}

func <(one: Student, two: Student) -> Bool {
    return one.name < two.name
}

func <=(one: Student, two: Student) -> Bool {
    return one.name <= two.name
}

func >(one: Student, two: Student) -> Bool {
    return one.name > two.name
}

func >=(one: Student, two: Student) -> Bool {
    return one.name >= two.name
}

var stu1 = Student(name: "Luo Hao", age: 35)
var stu2 = Student(name: "Wang Dachui", age: 18)

let minStu = myMin(stu1, stu2)
print(minStu.name)

var x = "hello", y = "good"
mySwap(&x, &y)
print(x, y)
print(myMin(x, y))

var a = 3.5, b = 1.2345
mySwap(&a, &b)
print(a, b)
print(myMin(a, b))

var c = 10, d = 100
mySwap(&c, &d)
print(c, d)
print(myMin(c, d))

// Swift中的類(lèi)、結(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())
}

  • 泛型類(lèi)/結(jié)構(gòu)/枚舉

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

  • 泛型限定
  • where子句

錯(cuò)誤處理

enum MyError: ErrorType {
    case A
    case B
    case C
}
  • throw
  • throws / rethrows
  • do
  • catch
  • try
    示例:
// 定義一個(gè)遵循ErrorType協(xié)議的枚舉
// 通過(guò)不同的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í)必須要寫(xiě)上throws關(guān)鍵字
    // throws關(guān)鍵字是提醒方法的調(diào)用者方法可能會(huì)出狀況 調(diào)用時(shí)要寫(xiě)try
    init(num: Int, den: Int) throws {
        _num = num
        _den = den
        if _den == 0 {
            // 如果程序中出現(xiàn)問(wèn)題就拋出錯(cuò)誤(異常)
            // 被throw關(guān)鍵字拋出的必須是遵循ErrorType協(xié)議的東西
            throw FractionError.ZeroDenominator
        }
        else {
            simplify()
            normalize()
        }
    }
    
    func add(other: Fraction) -> Fraction {
        // 如果能夠確保方法調(diào)用時(shí)不出異常那么可以在try關(guān)鍵字后加!
        // 這樣就可以在不寫(xiě)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)算符重載(為自定義的類(lèi)型定義運(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)
}

func foo() {
    // 如果能夠保證代碼不出錯(cuò)可以在try后面加!
    // 如果不確定代碼是否出錯(cuò)可以在try后面加?
    // 需要注意的是有?的地方會(huì)產(chǎn)生Optional(可空類(lèi)型)
    // 稍后可能還需要對(duì)可空類(lèi)型進(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("無(wú)效的分?jǐn)?shù)無(wú)法進(jìn)行加法運(yùn)算")
    }
}

foo()


// 對(duì)于可能出狀況的代碼要放在do...catch中執(zhí)行
// 在可能出狀況的方法前還要寫(xiě)上try表示嘗試著執(zhí)行
// 如果在do中沒(méi)有出現(xiàn)任何狀況那么catch就不會(huì)執(zhí)行
// 如果do中出現(xiàn)了狀況代碼就不會(huì)再向下繼續(xù)執(zhí)行而是轉(zhuǎn)移到catch中
// 在do的后面可以跟上多個(gè)catch用于捕獲不同的異常狀況 但是最多只有一個(gè)catch會(huì)被執(zhí)行
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ò)了! 我也不知道什么問(wèn)題")
}


邊角知識(shí)

  • ARC

    通過(guò)一個(gè)示例來(lái)理解ARC:

import Foundation

class GrandFather {
    
}

class Father: GrandFather {
    
}

class Son: Father {
    
    override init() {
        // 可以調(diào)用Father中的初始化方法
        // 不能調(diào)用GrandFather中的初始化方法
    }
}

class Person {
    var name: String
    var age: Int
    
    // 指派構(gòu)造器前面加上required可以將構(gòu)造器指定為必要構(gòu)造器
    // 所謂的必要構(gòu)造器意味著子類(lèi)也要提供一模一樣的構(gòu)造器
    // 指派構(gòu)造器(designated)
    required init(name: String, age: Int) {
        print("創(chuàng)建一個(gè)人!")
        self.name = name
        self.age = age
    }
    
    // 便利構(gòu)造器(convenience)
    convenience init() {
        self.init(name: "無(wú)名氏", age: 20)
    }
    
    deinit {
        print("人嗝屁了!")
    }
}

class Student: Person {
    var major: String
    
    required init(name: String, age: Int) {
        major = "未知"
        super.init(name: name, age: age)
    }
    
    convenience init(name: String, age: Int, major: String) {
        // 下面的語(yǔ)句必須寫(xiě)在調(diào)用自己的初始化方法之后否則major屬性會(huì)被賦上不正確的值
        // self.major = major
        self.init(name: name, age: age)
        self.major = major
        // 初始化的第一階段
        //  1. 初始化自己特有的屬性
//        self.major = major
//        // 子類(lèi)只能調(diào)用直接父類(lèi)的構(gòu)造器
//        // 子類(lèi)構(gòu)造器必須調(diào)用父類(lèi)的非便利構(gòu)造器(指派構(gòu)造器)
//        // super.init()    // compiler error
//        //  2. 調(diào)用父類(lèi)的初始化方法
//        super.init(name: name, age: age)
//        // 初始化的第二階段
//        // 此處可以調(diào)用對(duì)象的方法因?yàn)閷?duì)象已經(jīng)完成了初始化
//        study()
    }
    
    func study() {
        print("\(name)正在學(xué)習(xí).")
    }
    
    deinit {
        print("學(xué)生對(duì)象嗝屁了!")
    }
}

class Teacher: Person {
    
    
    deinit {
        print("老師對(duì)象嗝屁了!")
    }
}

//// 創(chuàng)建一個(gè)學(xué)生對(duì)象 然后用stu1去引用它 所以此時(shí)學(xué)生對(duì)象引用計(jì)數(shù)為1
//var stu1: Student? = Student()
//// 此處沒(méi)有創(chuàng)建新的學(xué)生對(duì)象 原來(lái)的學(xué)生對(duì)象的引用計(jì)數(shù)+1
//var stu2 = stu1
//// 同上 原來(lái)的學(xué)生對(duì)象的引用計(jì)數(shù)+1
//var stu3 = stu2
//
//// 學(xué)生對(duì)象引用計(jì)數(shù)-1
//stu1 = nil
//// 學(xué)生對(duì)象引用計(jì)數(shù)-1
//stu2 = nil
//// 學(xué)生對(duì)象引用計(jì)數(shù)-1
//// 當(dāng)學(xué)生對(duì)象引用計(jì)數(shù)為0時(shí) ARC會(huì)自動(dòng)清理內(nèi)存釋放學(xué)生對(duì)象
//// ARC即時(shí)性的內(nèi)存清理 優(yōu)于Java中的Garbage Collection(垃圾回收)
//stu3 = nil

// var stu1: Student? = Student()
// weak修飾的引用(弱引用)不會(huì)增加引用計(jì)數(shù) 默認(rèn)是強(qiáng)引用(會(huì)增加引用計(jì)數(shù))
// weak var stu2 = stu1
// weak var stu3 = stu2

// stu1 = nil
// 如果想釋放內(nèi)存 程序員可以手動(dòng)將一個(gè)引用賦值為nil

// func foo() {
    // stu是一個(gè)局部變量/常量 在函數(shù)調(diào)用結(jié)束后局部變量就消失了
    // 所以學(xué)生對(duì)象的引用計(jì)數(shù)也就變成0了 所以會(huì)被ARC釋放掉
    // let stu = Student()
    // print(stu)
// }

// foo()

// 棧 - FILO 先進(jìn)后出的結(jié)構(gòu)
// 創(chuàng)建任何子類(lèi)對(duì)象的時(shí)候一定是先創(chuàng)建了父類(lèi)對(duì)象
// var stu: Person = Student()
// 引用轉(zhuǎn)移(會(huì)導(dǎo)致原來(lái)對(duì)象上的引用計(jì)數(shù)-1 新對(duì)象引用計(jì)數(shù)+1)
// stu = Teacher()
// stu = Person()

// Swift的自動(dòng)釋放池
// 通過(guò)向autoreleasepool函數(shù)中傳入一個(gè)閉包來(lái)實(shí)現(xiàn)
// autoreleasepool { () -> () in
    // 自動(dòng)釋放池中的對(duì)象引用在池的邊界會(huì)收到引用計(jì)數(shù)-1的消息
    // 將來(lái)做iOS開(kāi)發(fā)時(shí)如果某個(gè)地方會(huì)創(chuàng)建很多的臨時(shí)對(duì)象
    // 那么最好在此處設(shè)置一個(gè)自動(dòng)釋放池避免內(nèi)存瞬時(shí)峰值過(guò)高造成閃退
    // let stu1 = Student()
    // let stu2 = stu1
// }
// 離開(kāi)自動(dòng)釋放池時(shí) stu1會(huì)收到引用計(jì)數(shù)-1消息 stu2也會(huì)收到引用計(jì)數(shù)-1消息
  • 雙向關(guān)聯(lián)關(guān)系
    如果程序中出現(xiàn)了類(lèi)與類(lèi)之間雙向關(guān)聯(lián)關(guān)系 必須要將其中一端設(shè)置為weak引用
    否則將會(huì)形成循環(huán)引用導(dǎo)致ARC無(wú)法釋放內(nèi)存
    示例:
class Emp {
    // 推薦使用
    // 如果允許使用可空類(lèi)型通常使用weak來(lái)破除循環(huán)引用
    // 如果員工關(guān)聯(lián)的部門(mén)對(duì)象被釋放了那么dept會(huì)被賦值為nil
    // 如果要繼續(xù)給dept對(duì)象發(fā)消息程序不會(huì)崩潰
    // weak var dept: Dept?
    
    // 謹(jǐn)慎使用
    // 如果不允許使用可空類(lèi)型就必須使用unowned來(lái)破除循環(huán)引用
    // 需要注意的是如果員工對(duì)象關(guān)聯(lián)的部門(mén)對(duì)象被釋放了
    // 如果還要通過(guò)員工對(duì)象去操作它所關(guān)聯(lián)的部門(mén)對(duì)象將導(dǎo)致程序崩潰
    // EXC_BAD_ACCESS
    unowned var dept: Dept
    
    init(dept: Dept) {
        print("創(chuàng)建一個(gè)員工")
        self.dept = dept
    }
    
    deinit {
        print("銷(xiāo)毀一個(gè)員工")
    }
}

class Dept {
    var manager: Emp?
    
    init() {
        print("創(chuàng)建一個(gè)部門(mén)")
    }

    deinit {
        print("銷(xiāo)毀一個(gè)部門(mén)")
    }
}

func bar() {
    // let person = Person()
    let dept = Dept()
    let emp = Emp(dept: dept)
    dept.manager = emp
}

bar()

  • 正則表達(dá)式
  • 嵌套類(lèi)型
最后編輯于
?著作權(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)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,656評(píng)論 1 51
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young閱讀 4,171評(píng)論 1 10
  • 《歐陽(yáng)陽(yáng)光.全腦幸福課》第5節(jié):不完美的性,后面的都有你認(rèn)為的應(yīng)該 “你別無(wú)選擇,由她選擇。她像描繪圖形一樣扭動(dòng)腰...
    裴陽(yáng)陽(yáng)閱讀 317評(píng)論 0 0
  • 隨著技能和科研的不斷發(fā)展,我們我國(guó)在各個(gè)學(xué)術(shù)范疇的研發(fā)工作上,取得了令人歡喜的效果。 按傳輸媒質(zhì)的不同,可分...
    北侖情閱讀 1,612評(píng)論 0 0
  • 陰天 突然就想寫(xiě)日記了。希望自己能一直堅(jiān)持下去 就我個(gè)人而言 我記性是不太好的。我希望能記住一些比較美好或者不算太...
    68dcc05e8e38閱讀 235評(píng)論 0 0

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