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

個人感悟:一個月的學(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ù)&quot;一等公民&quot;函數(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)用子類特有方法

三個步驟

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

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

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

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

  1. 設(shè)計一個協(xié)議,讓被委托方遵循協(xié)議并實現(xiàn)協(xié)議中的方法
  2. 委托方有一個屬性是協(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)鍵有兩點:
  1. 抽象是關(guān)鍵(在設(shè)計系統(tǒng)的時候一定要設(shè)計好的協(xié)議);
  2. 封裝可變性(橋梁模式 - 將不同的可變因素封裝到不同的繼承結(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()

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

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

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

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