個(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ù)";一等公民";函數(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è)步驟
- 定義類(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,作用相同
- 方法(寫(xiě)到類(lèi)里面的函數(shù)就是方法或者說(shuō)跟對(duì)象綁定的行為)
- 構(gòu)造器
- 指派構(gòu)造器
- 便利構(gòu)造器(convenience)
- 必要構(gòu)造器(required)
- 創(chuàng)建對(duì)象
- 給對(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é)議編程)
- 聲明變量的類(lèi)型時(shí)應(yīng)該盡可能使用協(xié)議類(lèi)型
- 聲明方法參數(shù)類(lèi)型時(shí)應(yīng)該盡可能使用協(xié)議類(lèi)型
- 聲明方法返回類(lèi)型時(shí)應(yīng)該盡可能使用協(xié)議類(lèi)型
用協(xié)議實(shí)現(xiàn)委托回調(diào)
一個(gè)對(duì)象想做某件事但是自身沒(méi)有能力做這件事,就可以使用委托回調(diào),具體步驟是:
- 設(shè)計(jì)一個(gè)協(xié)議,讓被委托方遵循協(xié)議并實(shí)現(xiàn)協(xié)議中的方法
- 委托方有一個(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):
- 抽象是關(guān)鍵(在設(shè)計(jì)系統(tǒng)的時(shí)候一定要設(shè)計(jì)好的協(xié)議);
- 封裝可變性(橋梁模式 - 將不同的可變因素封裝到不同的繼承結(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)型