Swift——第三周的爬坑之路

協(xié)議

1.定義:協(xié)議是方法的集合,可以把看似不相關的對象的公共行為放到一個協(xié)議中。
2.協(xié)議自身的意義:Swift中的繼承是單一繼承(一個類只能有一個父類), 如果希望讓一個類具備多重能力可以使用協(xié)議來實現(xiàn)。
3.協(xié)議在Swift開發(fā)中大致有三種作用:

  • 能力 - 遵循了協(xié)議就意味著具備了某種能力。
  • 約定 - 遵循了協(xié)議就一定要實現(xiàn)協(xié)議中的方法。
  • 角色 - 一個類可以遵循多個協(xié)議, 一個協(xié)議可以被多個類遵循, 遵循協(xié)議就意味著扮演了某種角色, 遵循多個協(xié)議就意味著可以扮演多種角色。

4.實例:

//首先進行協(xié)議的創(chuàng)建
protocol Flyable {
    
    func fly()
}

protocol Fightable {
    
    func fight()
}
//再進行實體類的創(chuàng)建并繼承協(xié)議
class Bird: Flyable {

    func fly() {
        print("鳥兒扇動翅膀飛行.")
    }
}

class Boxer: Fightable {
    
    func fight() {
        print("正在進行格斗.")
    }
}

而此時想要建立一個超人類,卻發(fā)現(xiàn)協(xié)議不“夠”用了,那么可以進行協(xié)議的擴展和協(xié)議之間的繼承

//協(xié)議的擴展 - 可以在協(xié)議擴展中給協(xié)議中的方法提供默認實現(xiàn)
// 也就是說如果某個類遵循了協(xié)議但是沒有實現(xiàn)這個方法就直接使用默認實現(xiàn)
// 那么這個方法也就相當于是一個可選方法(可以實現(xiàn)也可以不實現(xiàn))
extension Fightable {
    
    func fight() {
        print("正在打架")
    }
}


// 協(xié)議的繼承
protocol Super: Flyable, Fightable {
    
    func dive()
}



//創(chuàng)建超人類
class Superman: Super {
    
    func fly() {
        print("超人使用超能力飛行.")
    }
    
//    func fight() {
//        print("超人正在和邪惡勢力干仗.")
//    }
    
    func dive() {
        print("超人正在潛水.")
    }
}

協(xié)議的總結:

1.依賴倒轉原則(面向協(xié)議編程)

  • 聲明變量的類型時應該盡可能使用協(xié)議類型
  • 聲明方法參數(shù)類型時應該盡可能使用協(xié)議類型
  • 聲明方法返回類型時應該盡可能使用協(xié)議類型

2.協(xié)議的開閉原則:

  • 協(xié)議中全是抽象概念(只有聲明沒有實現(xiàn)) 遵循協(xié)議的類可以各自對協(xié)議中的計算屬性和方法給出自己的實現(xiàn)版本 這樣當我們面向協(xié)議編程時就可以把多態(tài)的優(yōu)勢發(fā)揮到淋漓盡致 可以寫出更通用更靈活的代碼(符合開閉原則)

3.接口(協(xié)議)隔離原則: 協(xié)議的設計要小而專不要大而全

結構

1.定義:用于保存基層數(shù)據的數(shù)據結構;與類相似,只是將定義的關鍵字改為了struct。
2.與類的區(qū)別

  • 區(qū)別 1: 結構的對象是值類型, 類的對象是引用類型
  • 值類型在賦值的時候會在內存中進行對象的拷貝
  • 引用類型在賦值的時候不會進行對象拷貝只是增加了一個引用
  • 區(qū)別 2: 結構會自動生成初始化方法
  • 區(qū)別 3: 結構中的方法在默認情況下是不允許修改結構中的屬性除非加上mutating關鍵字
    3.代碼演示:
class Student1 {
    var name: String
    var age: Int
    var tel: String?
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func getOlder() {
        age += 1
    }
    
    func study(courseName: String) {
        print("\(name)正在學習.")
    }
}
struct Student2 {
    var name: String
    var age: Int
    
    func study(courseName: String) {
        print("\(name)正在學習.")
    }
    

    mutating func getOlder() {
        age += 1
    }
}

// 引用類型的類
let stu1 = Student1(name: "Xyk_", age: 18)
var stu3 = stu1     // 此處內存中仍然只有一個學生對象
stu3.name = "-kyX"
stu3.age = 15
print(stu1.name)
print(stu1.age)

// 值類型的結構
let stu2 = Student2(name: "Xyk_kyX", age: 18)
var stu4 = stu2     // 此處內存中會復制一個新的學生對象
stu4.name = "Xyk_kyX_Xyk"
stu4.age = 15
print(stu2.name)
print(stu2.age)
//打印結果:
            -kyX
            15
            Xyk_kyX
            18

//這里的打印結果可以看出:
//1:用struct創(chuàng)建的學生在被“賦值”給學生的以后,沒有被更改姓名和年齡
//2:用類創(chuàng)建的學生在被“賦值”給學生的以后,被更改姓名和年齡

釋放內存

代碼演示1:

class Person {
    var name: String
    var age: Int
    
    // 指派構造器前面加上required可以將構造器指定為必要構造器
    // 所謂的必要構造器意味著子類也要提供一模一樣的構造器
    // 指派構造器(designated)
    required init(name: String, age: Int) {
        print("創(chuàng)建一個人!")
        self.name = name
        self.age = age
    }
    
    // 便利構造器(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) {
        // 下面的語句必須寫在調用自己的初始化方法之后否則major屬性會被賦上不正確的值
        // self.major = major
        self.init(name: name, age: age)
        self.major = major
        // 初始化的第一階段
        //  1. 初始化自己特有的屬性
//        self.major = major
//        子類只能調用直接父類的構造器
//        子類構造器必須調用父類的非便利構造器(指派構造器)
//        super.init()    // compiler error
//        2. 調用父類的初始化方法
//        super.init(name: name, age: age)
//        初始化的第二階段
//        此處可以調用對象的方法因為對象已經完成了初始化
//        study()
    }
    
    func study() {
        print("\(name)正在學習.")
    }
    
    deinit {
        print("學生對象嗝屁了!")
    }
}

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

// 創(chuàng)建一個學生對象 然后用stu1去引用它 所以此時學生對象引用計數(shù)為1
var stu1: Student? = Student()
// 此處沒有創(chuàng)建新的學生對象 原來的學生對象的引用計數(shù)+1
var stu2 = stu1
// 同上 原來的學生對象的引用計數(shù)+1
var stu3 = stu2

// 學生對象引用計數(shù)-1
stu1 = nil
// 學生對象引用計數(shù)-1
stu2 = nil
// 學生對象引用計數(shù)-1
// 當學生對象引用計數(shù)為0時 ARC會自動清理內存釋放學生對象
// ARC即時性的內存清理 優(yōu)于Java中的Garbage Collection(垃圾回收)
stu3 = nil

代碼演示2:

class Emp {
    // 推薦使用
    // 如果允許使用可空類型通常使用weak來破除循環(huán)引用
    // 如果員工關聯(lián)的部門對象被釋放了那么dept會被賦值為nil
    // 如果要繼續(xù)給dept對象發(fā)消息程序不會崩潰
    // weak var dept: Dept?
    
    // 謹慎使用
    // 如果不允許使用可空類型就必須使用unowned來破除循環(huán)引用
    // 需要注意的是如果員工對象關聯(lián)的部門對象被釋放了
    // 如果還要通過員工對象去操作它所關聯(lián)的部門對象將導致程序崩潰
    // 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()

泛型:

1.定義一個虛擬類型T, 調用函數(shù)時根據傳入的參數(shù)類型來決定T到底是什么
2.泛型限定:<T: Comparable>限定T類型必須是遵循了Comparable協(xié)議的類型
3.讓類型不再是程序中的硬代碼

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
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容