前言:原本這周應(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.程序存儲執(zhí)行
變量和常量
定義變量和常量是為了保存數(shù)據(jù)此叠,變量和常量就是某種類型的值的存儲空間
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í)如果可以的話直接使用類型,不用手動指定類型牧抵;2.如果可以的話應(yīng)該盡可能的使用常量而不是變量
語言元素
var a: Int = 10
關(guān)鍵字:有特殊含義的單詞
標(biāo)識符:給變量笛匙、常量、函數(shù)犀变、類妹孙、結(jié)構(gòu)、協(xié)議获枝、枚舉蠢正、方法、屬性等起的名字
- 字母(Unicod字符)省店、數(shù)字嚣崭、下劃線笨触,數(shù)字不能開頭
- 大小寫敏感(區(qū)分大小寫)
- 不能使用關(guān)鍵字做標(biāo)識符
- 使用駝峰命名法(命名變量、常量雹舀、函數(shù)芦劣、方法、屬性第一個(gè)單詞小寫说榆,從第二個(gè)單詞開始每個(gè)單詞首字母大寫虚吟;命名類、結(jié)構(gòu)签财、協(xié)議串慰、枚舉每個(gè)單詞首字母都要大寫)
- 見名知意
- 命名私有的屬性和方法時(shí)以下劃線開頭
運(yùn)算符: Swift中的運(yùn)算符其實(shí)都是函數(shù)
- 賦值運(yùn)算符:=、+=唱蒸、-=邦鲫、......
- 算術(shù)運(yùn)算符:+、-神汹、*掂碱、/、%
- 比較運(yùn)算符:==慎冤、!=、<沧卢、<=蚁堤、>、>=
- 邏輯運(yùn)算符:&&但狭、||披诗、!
- 條件運(yùn)算符:?:
- 其它運(yùn)算符:[]、.立磁、??呈队、?、!
字面(常)量
- 整數(shù)字面量: 10唱歧、1_123_456宪摧、0x10、0o10颅崩、0b10
- 小數(shù)字面量: 123.45几于、1.234e2、0xab.cdp2
- 字符字面量: "c"沿后、"\n"沿彭、"\u{41}"、"\u{9a86}"
- 字符串字面量:"hellow"尖滚、"caf\u{e9}"
- 布爾字面量:true喉刘、false
- 空值字面量:nil
- 類型字面量:String.self瞧柔、UILable.self
分隔符:將不同的語言元素符號分開
說明:Swift中每個(gè)語句后面的分號是可寫可不寫的,寫代碼時(shí)盡量保證一行只有一條語句這樣就可以省略掉分號
分支和循環(huán)
分支
- if...else...
//輸入年睦裳、月判斷該月有多少天
print("請輸入年和月用空格隔開(例如: 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只雞鬓催,請列出所有可能的結(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)該盡量減少對break和continue的使用宇驾,因?yàn)樗鼈儾粫屇愕某绦蜃兊酶谩?/p>
綜合案例
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("請下注: ", 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
for i in 0..<array1.count {
print(array1[i])
}
- 方式2
for temp in array1 {
print(temp)
}
for temp in array1[1...3] {
print(temp)
}
說明:for-in循環(huán)是一個(gè)只讀循環(huán),這也就意味著再循環(huán)的過程中不能對數(shù)組中的元素進(jìn)行修改
- 方式3
for (i, temp) in array1.enumerate() {
print("\(i).\(temp)")
}
提醒:操作數(shù)組是最主要的是不要越界訪問元素筝尾。數(shù)組對象的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])的平均成績?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])課的平均成績?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沒有的元素)
字典
字典是以鍵值對的方式保存數(shù)據(jù)的容器苞俘,字典中的每個(gè)元素都是鍵值對的組合盹沈,通過鍵可以找到對應(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)")
}
重要操作
- 排序
- sort
- 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]
//通過映射對數(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ù)前要加上&符號
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ù)前要加上&符號
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中與之對應(yīng)的概念叫block墓卦。如果一個(gè)函數(shù)的參數(shù)類型是函數(shù)我們可以傳入一個(gè)閉包倦春;如果一個(gè)函數(shù)的返回類型是函數(shù)我們可以返回一個(gè)閉包;如果一個(gè)類的某個(gè)屬性是函數(shù)我們也可以將一個(gè)閉包表達(dá)式賦值給它落剪。
{ ([參數(shù)列表]) [-> 返回類型] in 代碼 }
面向?qū)ο缶幊蹋∣OP)
基本概念
對象:接受消息的單元睁本,對象是一個(gè)具體的概念
類:對象的藍(lán)圖和模板,類是一個(gè)抽象概念
消息:對象之間通信的手段忠怖,通過給對象發(fā)消息可以讓對象執(zhí)行對應(yīng)的操作來解決問題
四大支柱
抽象:定義類的過程就是一個(gè)抽象的過程呢堰,需要做數(shù)據(jù)抽象和行為抽象,數(shù)據(jù)抽象找到對象的屬性(保存對象狀態(tài)的存儲屬性)凡泣,行為抽象找到對象的方法(可以給對象發(fā)的消息)
封裝:
- 觀點(diǎn)1:我們在類中寫方法其實(shí)就是在封裝API暮胧,方法內(nèi)部實(shí)現(xiàn)可能會很復(fù)雜,但是這些對調(diào)用者來說是不可見的问麸,調(diào)用只能看到方法有一個(gè)簡單清晰的接口。
- 觀點(diǎn)2:將對象的屬性和操作這些屬性的方法綁定在一起钞翔。
- 觀點(diǎn)3:隱藏一切可以隱藏的實(shí)現(xiàn)細(xì)節(jié)严卖,只提供簡單清晰的接口(界面)。
繼承:
從已有的類創(chuàng)建新類的過程
提供繼承信息的稱為父類(超類/基類)
得到繼承信息的稱為子類(派生類/衍生類)
通常子類除了得到父類的繼承信息還會增加一些自己特有的東西
所以子類的能力一定比父類更強(qiáng)大
繼承的意義在于子類可以復(fù)用父類的代碼并且增強(qiáng)系統(tǒng)現(xiàn)有的功能
注意:
1.可以將子類型的對象賦值給父類型的變量(因?yàn)樽宇惛割愔g是IS-A關(guān)系)
例如:學(xué)生是人, 老師是人, 所以學(xué)生和老師的對象可以賦值給人類型的變量
2.如果要將父類型的變量轉(zhuǎn)換成子類型需要用as運(yùn)算符進(jìn)行類型轉(zhuǎn)換
- 如果能夠確認(rèn)父類型的變量中就是某種子類型的對象可以用as!進(jìn)行轉(zhuǎn)換
- 如果不確定父類型的變量中是哪種子類型可以用as?嘗試轉(zhuǎn)換(可以通過if+as?將
父類型安全的轉(zhuǎn)換成子類型然后再調(diào)用子類特有方法)
多態(tài):
同樣的對象類型(Pet類型)接收相同的消息(調(diào)用相同的方法)
但是做了不同的事情 這就是多態(tài)(polymorphism)
- 實(shí)現(xiàn)多態(tài)的關(guān)鍵步驟:
- 方法重寫(子類在繼承父類的過程中對父類已有的方法進(jìn)行重寫, 而且不同的子類給出各自不同的實(shí)現(xiàn)版本)
- 對象造型(將子類對象當(dāng)成父類型來使用)
- 重要方式
重載 - overload
重寫 - override
三個(gè)步驟
- 定義類
- 數(shù)據(jù)抽象
- 存儲屬性
- 行為抽象
- 方法(寫到類里面的函數(shù)或者說跟對象綁定的行為就是方法)
- 對象方法
- 類方法(發(fā)給類的消息與對象狀態(tài)無關(guān))
- 方法(寫到類里面的函數(shù)或者說跟對象綁定的行為就是方法)
- 計(jì)算屬性
- 構(gòu)造器
- 指派構(gòu)造器
- 便利構(gòu)造器
- 必要構(gòu)造器
- 數(shù)據(jù)抽象
- 創(chuàng)建對象
- 給對象發(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ā)給類的消息與對象狀態(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
}
// 對象方法(發(fā)給對象的消息與對象狀態(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)建對象前先調(diào)用類方法判定給定的三條邊能否構(gòu)成三角形
// 類方法是發(fā)給類的消息所以不用創(chuàng)建對象直接通過類名調(diào)用
if Triangle.isValid(a, b, c) {
let t = Triangle(a: a, b: b, c: c)
// 對象方法是發(fā)給對象的消息要先創(chuàng)建對象才能調(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)的對象是值類型, 類的對象是引用類型
區(qū)別2: 結(jié)構(gòu)會自動生成初始化方法
區(qū)別3: 結(jié)構(gòu)中的方法在默認(rèn)情況下是不允許修改結(jié)構(gòu)中的屬性除非加上mutating關(guān)鍵字
值類型在賦值的時(shí)候會在內(nèi)存中進(jìn)行對象的拷貝
引用類型在賦值的時(shí)候不會進(jìn)行對象拷貝只是增加了一個(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)建的對象都是放在堆上的
- 特點(diǎn): 大、慢
靜態(tài)區(qū) (static area)
- 數(shù)據(jù)段 - 全局量
- 只讀數(shù)據(jù)段 - 常量
- 代碼段 - 函數(shù)和方法
- 擴(kuò)展(extension)
如果在某個(gè)特定的應(yīng)用場景中你發(fā)現(xiàn)現(xiàn)有的類缺少了某項(xiàng)功能那么可以通過類擴(kuò)展(extension)的方式現(xiàn)場添加這項(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)
- 直接通過對象的下標(biāo)來對其進(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)用者方法可能會出狀況 調(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
// 對于可能出狀況的代碼要放在do...catch中執(zhí)行
// 在可能出狀況的方法前還要寫上try表示嘗試著執(zhí)行
// 如果在do中沒有出現(xiàn)任何狀況那么catch就不會執(zhí)行
// 如果do中出現(xiàn)了狀況代碼就不會再向下繼續(xù)執(zhí)行而是轉(zhuǎn)移到catch中
// 在do的后面可以跟上多個(gè)catch用于捕獲不同的異常狀況 但是最多只有一個(gè)catch會被執(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后面加?
// 需要注意的是有?的地方會產(chǎn)生Optional(可空類型)
// 稍后可能還需要對可空類型進(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)于就是方法)
}
- 能力 - 遵循了協(xié)議就意味著具備了某種能力
- 約定 - 遵循了協(xié)議就一定要實(shí)現(xiàn)協(xié)議中的方法
- 角色 - 一個(gè)類可以遵循多個(gè)協(xié)議, 一個(gè)協(xié)議可以被多個(gè)類遵循, 遵循協(xié)議就意味著扮演了某種角色, 遵循多個(gè)協(xié)議就意味著可以扮演多種角色
Swift中的繼承是單一繼承(一個(gè)類只能有一個(gè)父類), 如果希望讓一個(gè)類具備多重能力可以使用協(xié)議來實(shí)現(xiàn)(C++里面是通過多重繼承來實(shí)現(xiàn)的, 這是一種非常不好的做法)
依賴倒轉(zhuǎn)原則(面向協(xié)議編程)
- 聲明變量的類型時(shí)應(yīng)該盡可能使用協(xié)議類型
- 聲明方法參數(shù)類型時(shí)應(yīng)該盡可能使用協(xié)議類型
- 聲明方法返回類型時(shí)應(yīng)該盡可能使用協(xié)議類型
用協(xié)議實(shí)現(xiàn)委托回調(diào)
一個(gè)對象想做某件事情但是自身沒有能力做這件事可以使用委托回調(diào)
- 設(shè)計(jì)協(xié)議笆包,被委托方遵循協(xié)議實(shí)現(xiàn)方法
- 委托方有一個(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)行截圖:
泛型
讓類型不在是程序中的硬代碼(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)知識
- 泛型限定
<T: Comparable>限定T類型必須是遵循了Comparable協(xié)議的類型
- where子句
錯(cuò)誤處理
提示:請看上述求分?jǐn)?shù)的加減乘除運(yùn)算的例子
目標(biāo):只要明白以下關(guān)鍵字的用法即可
- throw
- throw / rethrows
- do
- catch
- try
邊角知識
- ARC - 自動引用計(jì)數(shù)
- 正則表達(dá)式
- 嵌套類型
--更多精彩內(nèi)容請關(guān)注:Youth丶夏夏--