個人感悟:一個月的學(xué)習(xí),學(xué)了很多內(nèi)容,但是并沒能很好的消化,所以說現(xiàn)在腦子里還是一片混亂,不能很好的將學(xué)習(xí)過的東西應(yīng)用在編寫程序上,所以說必須要一遍遍的去敲代碼練習(xí),fighting!
語言基礎(chǔ)
程序是指令的集合,寫程序就是寫一系列的指令去控制計算機做我們想做的事情。
編譯:將程序設(shè)計語言轉(zhuǎn)換成計算機能夠理解的機器語言或者某種中間代碼的過程。
馮諾依曼體系結(jié)構(gòu)的計算機:
1.使用二進制
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有非常強大的類型推斷,所以定義變量或常量時如果可以的話應(yīng)該直接使用類型推斷不用手動指定類型;2.如果看可以的話應(yīng)該盡可能使用常量而不是變量。
語言元素
var a: Int = 10
關(guān)鍵字:有特殊含義的單詞
標(biāo)識符:給變量、常量、函數(shù)、類、結(jié)構(gòu)、協(xié)議、枚舉、方法、屬性等起的名字
1.字母(Unicode字符)、數(shù)字、下劃線,數(shù)字不能開頭
2.大小寫敏感(區(qū)分大小寫)
3.不能使用關(guān)鍵字做標(biāo)識符
4.使用駝峰命名法(命名變量、常量、函數(shù)、方法、屬性第一個單詞小寫,從第二個單詞開始每個單詞首字母大寫;命名類、結(jié)構(gòu)、協(xié)議、枚舉每個單詞首字母都要大寫)
5.見名知意
6.命名私有的屬性和方法時以下劃線開頭
運算符:Swift中的運算符其實都是函數(shù)
1.賦值運算符:=、 +=、 -=、 ......
2.算術(shù)運算符:+、 -、 *、 /、 %
3.比較運算符:==、 !=、 <、 <=、 >、 >=
4.邏輯運算符:&&、||、 !
5.條件(三元)運算符:? :
6.其他運算符:[]、 .、 ??、 ?、 !、
字面(常)量:
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.類型字面量:String.self、UILabel.self
分隔符:將不同的語言元素符號分開
說明:Swift中每個語句后面的分號是可寫可不寫,寫代碼時盡量保證一行只有一條語句這樣就可以省略掉分號。
分支和循環(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è)人員等級評定
print("請輸入你的月薪: ", terminator: "")
let salary = inputInt()
if salary >= 0 {
switch salary {
case 0:
print("你是一個無業(yè)游民")
case 1...30000:
print("你是一個小碼畜")
case 30001...60000:
print("你是一個小碼奴")
case 60001...100000:
print("你是一個大碼農(nóng)")
case 100001...200000:
print("你是一個光榮的IT民工")
case 200001...500000:
print("你是一個優(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)
// 簡單選擇排序:每次從剩下的元素中找最小的元素放到對應(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 // 個位
let b = i / 10 % 10 // 十位
let c = i / 100 // 百位
if i == cube(a) + cube(b) + cube(c) {
print(i)
}
}
窮舉法:窮盡所有可能性直到找到正確答案。
下面的程序?qū)崿F(xià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)")
}
}
}
說明:在循環(huán)中可以使用break關(guān)鍵字來提前終止循環(huán),也可以使用continue關(guān)鍵字使循環(huán)直接進入下一輪,但是應(yīng)該盡量減少對break和continue的使用,因為他們不會讓你的程序變得更好
綜合案例:Craps賭博游戲
游戲規(guī)則:玩家搖色子,如果第一次搖出了7點或11點,玩家勝;如果搖出了2點、3或12點,莊家勝;其他點數(shù)游戲繼續(xù)。再繼續(xù)的過程中玩家重新?lián)u色子,如果搖出了7點,莊家勝;如果搖出了第一次搖的點數(shù),玩家勝;否則玩家繼續(xù)搖色子直到分出勝負。
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("請下注:", terminator: "")
debt = inputInt()
} while debt <= 0 || debt > money
let firstPoint = roll() + roll()
print("玩家搖出了\(firstPoint)點")
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)點")
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)存空間保存多個同類型的元素的容器,因為數(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)
}
說明:for-in循環(huán)是一個只讀循環(huán),這也就意味著在循環(huán)的過程中不能對數(shù)組中的元素進行修改
3.方式3:
for (i, temp) in array3.enumerate() {
if i == 0 {
array3[i] = 1
}
print("\(i). \(temp)")
}
提醒:操作數(shù)組時最重要的式不要越界訪問元素。數(shù)組對象的count屬性表明了數(shù)組中有多少個元素,那么有效的索引(下標(biāo))范圍是0到count-1
數(shù)組中的元素也可以是數(shù)組,因此我們可以構(gòu)造多維數(shù)組。最常見的是二維數(shù)組,它相當(dāng)于是一個有行有列的數(shù)組,數(shù)組中的每個元素代表一行,該數(shù)組中的每個元素代表行里面的列。二維數(shù)組可以模擬現(xiàn)實世界中的表格、數(shù)學(xué)上的矩陣、棋牌類游戲的棋盤、格子類游戲的地圖(植物大戰(zhàn)僵尸),所以在實際開發(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)存中是離散的,集合中的元素通過計算Hash Code(哈希碼或散列碼)來決定存放在內(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()
- 集合運算(交集、并集、差集)
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沒有的元素):
print(a.subtract(b)) // 輸出為:[2, 1]
字典
字典是以鍵值對的方式保存數(shù)據(jù)的容器,字典中的每個元素都是鍵值對組合,通過鍵可以找到對應(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
說明:排序方法的參數(shù)是一個閉包(closure),該閉包的作用是比較數(shù)組中兩個元素的大小。
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, 45, 12, 89, 98, 55, 7]
// 篩選掉不滿足條件的數(shù)據(jù)
let newArray1 = array.filter {$0 > 50}
print(newArray) // [89, 98, 55]
- 映射
let array = [23, 45, 12, 89, 98, 55, 7]
// 通過映射對數(shù)據(jù)進行變換處理
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ù)是獨立的可重復(fù)使用的功能模塊,如果程序中出現(xiàn)了大量的重復(fù)代碼,通常都可以將這部分功能封裝成一個獨立的函數(shù)。在Swift中,函數(shù)";一等公民";函數(shù)可以作為類型來使用,也就是說函數(shù)可以賦值給一個變量或常量,可以將函數(shù)作為函數(shù)的參數(shù)或者返回值,還可以使用高階函數(shù)。
func 函數(shù)名([參數(shù)1:類型, 參數(shù)2: 類型,...]) [throws | rethrows] [-> 返回類型] {
函數(shù)的執(zhí)行體
[return 表達式]
}
函數(shù)名(外部參數(shù)名 內(nèi)部參數(shù)名: 類型, 外部參數(shù)名 內(nèi)部參數(shù)名: 類型)
外部參數(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類型的參數(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)
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
閉包就是沒有名字的函數(shù)或者稱之為函數(shù)表達式(Lambda表達式),Objectiv-C中與之對應(yīng)的概念叫block。如果一個函數(shù)的參數(shù)類型是函數(shù)我們可以傳入一個閉包;如果一個函數(shù)的返回類型是函數(shù)我們可以返回一個閉包;如果一個類的某個屬性是函數(shù)我們也可以講一個閉包表達式賦值給它。
{([參數(shù)列表]) [-> 返回類型] in 代碼 }
面向?qū)ο缶幊蹋∣OP)
基本概念
對象:接收消息的單元,對象是一個具體的的概念。
類:對象的藍圖和模板,累世一個抽象概念。
消息:對象之間通信的方式,通過給對象發(fā)消息可以讓對象執(zhí)行對應(yīng)的操作來解決問題。
四大支柱
抽象:定義類的過程就是一個抽象的過程,需要做數(shù)據(jù)抽象和行為抽象,數(shù)據(jù)抽象找到對象的屬性(保存對象狀態(tài)的存儲屬性),行為抽象找到對象的方法(可以給對象發(fā)消息)。
封裝:
- 觀點1:我們在類中寫方法其實就是在封裝API,方法的內(nèi)部實現(xiàn)可能會很復(fù)雜,但是這些對調(diào)用者來說是不可見的,調(diào)用只能看到方法有一個簡單清晰的接口。
- 觀點2:將對象的屬性和操作這些屬性的方法綁定在一起。
- 觀點3:隱藏一切可以隱藏的實現(xiàn)細節(jié),只提供簡單清晰的接口(界面)。
繼承:從已有的類創(chuàng)建新類的過程稱為繼承
多態(tài):同樣的對象類型接收相同的消息(調(diào)用相同的方法),但是做了不同的事情 這就是多態(tài)(polymorphism)
- 實現(xiàn)多態(tài)的關(guān)鍵步驟:
①方法重寫(子類在繼承父類的過程中對父類已有的方法進行重寫, 而且不同的子類給出各自不同的實現(xiàn)版本)
②對象造型(將子類對象當(dāng)成父類型來使用)
~可以通過if+as?將父類型安全的轉(zhuǎn)換成子類型然后再調(diào)用子類特有方法
三個步驟
- 定義類
- 數(shù)據(jù)抽象
- 存儲屬性
- 行為抽象
- 方法(寫到類里面的函數(shù)就是方法或者說跟對象綁定的行為)
- 對象方法:給對象法的消息要先創(chuàng)建對象才能調(diào)用,與對象狀態(tài)有關(guān)
- 類方法:給類法的消息所以不用創(chuàng)建對象直接通過類名調(diào)用,與對象的狀態(tài)無關(guān)的方法 方法前加 static或class,作用相同
- 方法(寫到類里面的函數(shù)就是方法或者說跟對象綁定的行為)
- 構(gòu)造器
- 指派構(gòu)造器
- 便利構(gòu)造器(convenience)
- 必要構(gòu)造器(required)
- 創(chuàng)建對象
- 給對象發(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
}
// 類方法(發(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)容
- 枚舉
~枚舉是定義符號常量的最佳方式
~符號常量總是優(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: 點數(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)的對象是值類型, 類的對象是引用類型
值類型在賦值的時候會在內(nèi)存中進行對象的拷貝
引用類型在賦值的時候不會進行對象拷貝只是增加了一個引用
結(jié)論: 我們自定義新類型時優(yōu)先考慮使用類而不是結(jié)構(gòu)除非我們要定義的是一種底層的數(shù)據(jù)結(jié)構(gòu)(保存其他數(shù)據(jù)的類型)
區(qū)別2: 結(jié)構(gòu)會自動生成初始化方法
區(qū)別3: 結(jié)構(gòu)中的方法在默認情況下是不允許修改結(jié)構(gòu)中的屬性除非加上mutating關(guān)鍵字
總結(jié):類和結(jié)構(gòu)的區(qū)別到底有哪些?什么時候應(yīng)該使用結(jié)構(gòu)?什么時候應(yīng)該使用類?
- 擴展(extension)
~如果在某個特定的應(yīng)用場景中你發(fā)現(xiàn)現(xiàn)有的類缺少了某項功能
那么可以通過類擴展(extension)的方式現(xià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
}
}
// 運算符重載(為自定義的類型定義運算符)
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)運算(subscript)
-
訪問修飾符
- private
- internal
- public
面向協(xié)議編程(POP)
協(xié)議
protocol 協(xié)議名[:父協(xié)議1,父協(xié)議2,...] {
// 方法的集合(計算屬性相當(dāng)于就是方法)
}
1.能力: 遵循了協(xié)議就意味著具備了某種能力
2.約定: 遵循了協(xié)議就一定要實現(xiàn)協(xié)議中的方法
3.角色: 一個類可以遵循多個協(xié)議, 一個協(xié)議可以被多個類遵循, 遵循協(xié)議就意味著扮演了某種角色, 遵循多個協(xié)議就意味著可以扮演多種角色
注意:Swift中的繼承是單一繼承(一個類只能有一個父類), 如果希望讓一個類具備多重能力可以使用協(xié)議來實現(xiàn)(C++里面是通過多重繼承來實現(xiàn)的, 這是一種非常狗血的做法)
依賴倒轉(zhuǎn)原則
依賴倒轉(zhuǎn)原則(面向協(xié)議編程)
- 聲明變量的類型時應(yīng)該盡可能使用協(xié)議類型
- 聲明方法參數(shù)類型時應(yīng)該盡可能使用協(xié)議類型
- 聲明方法返回類型時應(yīng)該盡可能使用協(xié)議類型
用協(xié)議實現(xiàn)委托回調(diào)
一個對象想做某件事但是自身沒有能力做這件事,就可以使用委托回調(diào),具體步驟是:
- 設(shè)計一個協(xié)議,讓被委托方遵循協(xié)議并實現(xiàn)協(xié)議中的方法
- 委托方有一個屬性是協(xié)議類型的,通過該屬性可以調(diào)用協(xié)議中的方法
示例:五子棋App
import UIKitA
// RenjuBoard.swift
/**
棋盤交叉點的狀態(tài)
- Space: 空格
- Black: 黑棋
- White: 白棋
*/
enum PointState {
case Space, Black, White
}
/// 棋盤
class RenjuBoard {
var board: [[PointState]]
var isBlackTurn = true
var isGameOver = false
init() {
board = [[PointState]](count: 15, repeatedValue: [PointState](count: 15, repeatedValue: .Space))
}
// 索引器語法 - 可以直接對棋盤對象做下標(biāo)運算來放置棋子
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條橫線和15條豎線來構(gòu)造一個棋盤
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()
// 繪制棋盤的邊框
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()
}
// 繪制棋盤上的棋子
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
// 有的時候某個對象要做某件事情但其自身又沒有能力做這件事情
// 這個時候就可以使用委托回調(diào)的編程模式讓別的對象來做這件事情
// 實現(xiàn)委托回調(diào)的編程模式有以下幾個步驟:
// 1. 設(shè)計一個協(xié)議(被委托方必須要遵循協(xié)議才能給別的對象當(dāng)委托)
protocol CanvasDelegate: class {
// 協(xié)議里面的方法就是要委托其他對象做的事情
func showMessage(canvas: Canvas, message: String)
}
class Canvas: UIView {
// 2. 委托方添加一個屬性其類型是遵循了協(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ù)說要廢掉
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. 自己做不了的事情委托給別的對象來做
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. 給畫布對象綁定委托(self就是視圖控制器對象它遵循了協(xié)議所以有充當(dāng)委托的能力也就是說可以扮演被委托方的角色)
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é)議就必須要實現(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
// 此處通過尾隨閉包來定義點擊確定按鈕后要做什么
canvas.clearBoard()
}
alertController.addAction(okAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
// deinit在銷毀對象的時候調(diào)用
deinit {
// 銷毀計時器
timer?.invalidate()
}
}
注意:委托方的協(xié)議類型的屬性通常是可空類型,因為要寫成弱引用(weak)。
其他
- 協(xié)議組合: protocol<協(xié)議1, 協(xié)議2, ...>
- 可選方法
- 協(xié)議擴展:對協(xié)議中的方法給出默認實現(xiàn),也就是說如果某個類遵循了協(xié)議但是沒有實現(xiàn)這個方法就直接使用默認實現(xiàn),那么這個方法也就相當(dāng)于是一個可選方法(可以實現(xiàn)也可以不實現(xiàn))
- 協(xié)議中全是抽象概念(只有聲明沒有實現(xiàn)) 遵循協(xié)議的類可以各自對協(xié)議中的計算屬性和方法給出自己的實現(xiàn)版本 這樣當(dāng)我們面向協(xié)議編程時就可以把多態(tài)的優(yōu)勢發(fā)揮到淋漓盡致 可以寫出更通用更靈活的代碼(符合開閉原則)
- 實現(xiàn)開閉原則最關(guān)鍵有兩點:
- 抽象是關(guān)鍵(在設(shè)計系統(tǒng)的時候一定要設(shè)計好的協(xié)議);
- 封裝可變性(橋梁模式 - 將不同的可變因素封裝到不同的繼承結(jié)構(gòu)中)
- 接口(協(xié)議)隔離原則: 協(xié)議的設(shè)計要小而專不要大而全,協(xié)議的設(shè)計也要高度內(nèi)聚
- 示例:打折策略協(xié)議
import Foundation
/**
* 打折策略協(xié)議
*/
protocol DiscountStrategy {
/**
計算折扣
- parameter price: 原價
- 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
}
}
}
/// 圖書
class Book {
var name: String
var price: Double
var type: String
// 四人幫設(shè)計模式 - 策略模式
var strategy: DiscountStrategy?
/**
初始化方法
- parameter name: 書名
- parameter price: 價格
- 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
}
}
}
/// 折后價格
var discountedPrice: Double {
get { return price - discountValue }
}
}
let booksArray = [
Book(name: "C語言程序設(shè)計", price: 24.0, type: "計算機"),
Book(name: "名偵探柯南", price: 98.5, type: "漫畫"),
Book(name: "Swift從入門到住院", price: 35.8, type: "計算機"),
Book(name: "黃岡數(shù)學(xué)密卷", price: 34.2, type: "教材"),
Book(name: "中國股市探秘", price: 58.5, type: "金融")
]
let discountDict: [String: DiscountStrategy] = [
"計算機": 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)》原價: ¥\(book.price)元")
print("《\(book.name)》折后價: ¥\(book.discountedPrice)元")
totalPrice += book.discountedPrice
totalDiscount += book.discountValue
}
print(String(format: "總計: ¥%.1f元", totalPrice))
print(String(format: "為您節(jié)省了: ¥%.1f元", totalDiscount))
泛型
讓類型不再是程序中的硬代碼(hard code),可以設(shè)計出更通用的代碼。
- 泛型函數(shù)
通過示例來理解泛型函數(shù)
import Foundation
// 泛型 (generic) - 讓類型不再是程序中的硬代碼(寫死的東西)
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
}
// 定義一個虛擬類型T, 調(diào)用函數(shù)時根據(jù)傳入的參數(shù)類型來決定T到底是什么
func mySwap<T>(inout a: T, inout _ b: T) {
let temp = a
a = b
b = temp
}
// 泛型限定
// <T: Comparable>限定T類型必須是遵循了Comparable協(xié)議的類型
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中的類、結(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())
}
- 泛型類/結(jié)構(gòu)/枚舉
相關(guān)知識
- 泛型限定
- where子句
錯誤處理
enum MyError: ErrorType {
case A
case B
case C
}
- throw
- throws / rethrows
- do
- catch
- try
示例:
// 定義一個遵循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)"
}
}
// 如果一個方法拋出了異常 那么在聲明方法時必須要寫上throws關(guān)鍵字
// throws關(guān)鍵字是提醒方法的調(diào)用者方法可能會出狀況 調(diào)用時要寫try
init(num: Int, den: Int) throws {
_num = num
_den = den
if _den == 0 {
// 如果程序中出現(xiàn)問題就拋出錯誤(異常)
// 被throw關(guān)鍵字拋出的必須是遵循ErrorType協(xié)議的東西
throw FractionError.ZeroDenominator
}
else {
simplify()
normalize()
}
}
func add(other: Fraction) -> Fraction {
// 如果能夠確保方法調(diào)用時不出異常那么可以在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
}
}
// 運算符重載(為自定義的類型定義運算符)
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() {
// 如果能夠保證代碼不出錯可以在try后面加!
// 如果不確定代碼是否出錯可以在try后面加?
// 需要注意的是有?的地方會產(chǎn)生Optional(可空類型)
// 稍后可能還需要對可空類型進行拆封, 拆封方式有二:
// 1. 不安全的做法: xxx!
// 2. 安全的做法: 用if let = xxx { }進行拆封
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("無效的分數(shù)無法進行加法運算")
}
}
foo()
// 對于可能出狀況的代碼要放在do...catch中執(zhí)行
// 在可能出狀況的方法前還要寫上try表示嘗試著執(zhí)行
// 如果在do中沒有出現(xiàn)任何狀況那么catch就不會執(zhí)行
// 如果do中出現(xiàn)了狀況代碼就不會再向下繼續(xù)執(zhí)行而是轉(zhuǎn)移到catch中
// 在do的后面可以跟上多個catch用于捕獲不同的異常狀況 但是最多只有一個catch會被執(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("出錯了! 我也不知道什么問題")
}
邊角知識
-
ARC
通過一個示例來理解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)造器意味著子類也要提供一模一樣的構(gòu)造器
// 指派構(gòu)造器(designated)
required init(name: String, age: Int) {
print("創(chuàng)建一個人!")
self.name = name
self.age = age
}
// 便利構(gòu)造器(convenience)
convenience init() {
self.init(name: "無名氏", 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) {
// 下面的語句必須寫在調(diào)用自己的初始化方法之后否則major屬性會被賦上不正確的值
// self.major = major
self.init(name: name, age: age)
self.major = major
// 初始化的第一階段
// 1. 初始化自己特有的屬性
// self.major = major
// // 子類只能調(diào)用直接父類的構(gòu)造器
// // 子類構(gòu)造器必須調(diào)用父類的非便利構(gòu)造器(指派構(gòu)造器)
// // super.init() // compiler error
// // 2. 調(diào)用父類的初始化方法
// super.init(name: name, age: age)
// // 初始化的第二階段
// // 此處可以調(diào)用對象的方法因為對象已經(jīng)完成了初始化
// study()
}
func study() {
print("\(name)正在學(xué)習(xí).")
}
deinit {
print("學(xué)生對象嗝屁了!")
}
}
class Teacher: Person {
deinit {
print("老師對象嗝屁了!")
}
}
//// 創(chuàng)建一個學(xué)生對象 然后用stu1去引用它 所以此時學(xué)生對象引用計數(shù)為1
//var stu1: Student? = Student()
//// 此處沒有創(chuàng)建新的學(xué)生對象 原來的學(xué)生對象的引用計數(shù)+1
//var stu2 = stu1
//// 同上 原來的學(xué)生對象的引用計數(shù)+1
//var stu3 = stu2
//
//// 學(xué)生對象引用計數(shù)-1
//stu1 = nil
//// 學(xué)生對象引用計數(shù)-1
//stu2 = nil
//// 學(xué)生對象引用計數(shù)-1
//// 當(dāng)學(xué)生對象引用計數(shù)為0時 ARC會自動清理內(nèi)存釋放學(xué)生對象
//// ARC即時性的內(nèi)存清理 優(yōu)于Java中的Garbage Collection(垃圾回收)
//stu3 = nil
// var stu1: Student? = Student()
// weak修飾的引用(弱引用)不會增加引用計數(shù) 默認是強引用(會增加引用計數(shù))
// weak var stu2 = stu1
// weak var stu3 = stu2
// stu1 = nil
// 如果想釋放內(nèi)存 程序員可以手動將一個引用賦值為nil
// func foo() {
// stu是一個局部變量/常量 在函數(shù)調(diào)用結(jié)束后局部變量就消失了
// 所以學(xué)生對象的引用計數(shù)也就變成0了 所以會被ARC釋放掉
// let stu = Student()
// print(stu)
// }
// foo()
// 棧 - FILO 先進后出的結(jié)構(gòu)
// 創(chuàng)建任何子類對象的時候一定是先創(chuàng)建了父類對象
// var stu: Person = Student()
// 引用轉(zhuǎn)移(會導(dǎo)致原來對象上的引用計數(shù)-1 新對象引用計數(shù)+1)
// stu = Teacher()
// stu = Person()
// Swift的自動釋放池
// 通過向autoreleasepool函數(shù)中傳入一個閉包來實現(xiàn)
// autoreleasepool { () -> () in
// 自動釋放池中的對象引用在池的邊界會收到引用計數(shù)-1的消息
// 將來做iOS開發(fā)時如果某個地方會創(chuàng)建很多的臨時對象
// 那么最好在此處設(shè)置一個自動釋放池避免內(nèi)存瞬時峰值過高造成閃退
// let stu1 = Student()
// let stu2 = stu1
// }
// 離開自動釋放池時 stu1會收到引用計數(shù)-1消息 stu2也會收到引用計數(shù)-1消息
- 雙向關(guān)聯(lián)關(guān)系
如果程序中出現(xiàn)了類與類之間雙向關(guān)聯(lián)關(guān)系 必須要將其中一端設(shè)置為weak引用
否則將會形成循環(huán)引用導(dǎo)致ARC無法釋放內(nèi)存
示例:
class Emp {
// 推薦使用
// 如果允許使用可空類型通常使用weak來破除循環(huán)引用
// 如果員工關(guān)聯(lián)的部門對象被釋放了那么dept會被賦值為nil
// 如果要繼續(xù)給dept對象發(fā)消息程序不會崩潰
// weak var dept: Dept?
// 謹慎使用
// 如果不允許使用可空類型就必須使用unowned來破除循環(huán)引用
// 需要注意的是如果員工對象關(guān)聯(lián)的部門對象被釋放了
// 如果還要通過員工對象去操作它所關(guān)聯(lián)的部門對象將導(dǎo)致程序崩潰
// EXC_BAD_ACCESS
unowned var dept: Dept
init(dept: Dept) {
print("創(chuàng)建一個員工")
self.dept = dept
}
deinit {
print("銷毀一個員工")
}
}
class Dept {
var manager: Emp?
init() {
print("創(chuàng)建一個部門")
}
deinit {
print("銷毀一個部門")
}
}
func bar() {
// let person = Person()
let dept = Dept()
let emp = Emp(dept: dept)
dept.manager = emp
}
bar()
- 正則表達式
- 嵌套類型