Swift第三周學(xué)習(xí)總結(jié)

[TOC]

協(xié)議

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

  1. 抽象是關(guān)鍵(在設(shè)計系統(tǒng)的時候一定要設(shè)計好的協(xié)議);
  2. 封裝可變性(橋梁模式 - 將不同的可變因素封裝到不同的繼承結(jié)構(gòu)中)

協(xié)議是方法的集合(計算屬性相當于就是方法), 可以把看似不相關(guān)的對象的公共行為放到一個協(xié)議中。
協(xié)議在Swift開發(fā)中大致有三種作用:

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

創(chuàng)建協(xié)議

protocol Flyable {
    
    func fly()
}

protocol Fightable {
    
    func fight()
}

協(xié)議的實現(xiàn)(一個類可以繼承多個協(xié)議)

class Superman: Fightable,Flyable  {

    func fight() {
        print("超人正在打怪獸.")
   }
    func fly() {
        print("超人使用超能力飛行.")
    }

}

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

extension Fightable {
    func fight() {
        print("正在打架")
    }
}

協(xié)議的繼承(協(xié)議可以繼承多個協(xié)議)

protocol NiuBi: Flyable, Fightable {
    func dive()
}

協(xié)議的組合

let array: [protocol<Flyable, Fightable>] = [
    Superman()
]

for obj in array {
    obj.fly()
    obj.fight()
}

運行結(jié)果

屏幕快照 2016-08-19 下午5.03.05.png

依賴倒轉(zhuǎn)原則(面向協(xié)議編程)

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

結(jié)構(gòu)

Swift中的類和結(jié)構(gòu)體定義的語法是非常相似的。類使用class關(guān)鍵詞定義類,使用struct關(guān)鍵詞定義結(jié)構(gòu)體,它們的語法格式如下:

class 類名 {
    定義類的成員
}
struct 結(jié)構(gòu)體名 {
    定義結(jié)構(gòu)體的成員
}

結(jié)構(gòu)與類的區(qū)別:

1: 結(jié)構(gòu)的對象是值類型, 類的對象是引用類型

  • 值類型在賦值的時候會在內(nèi)存中進行對象的拷貝
  • 引用類型在賦值的時候不會進行對象拷貝只是增加了一個引用

** 結(jié)論**: 我們自定義新類型時優(yōu)先考慮使用類而不是結(jié)構(gòu)除非我們要定義的是一種底層的數(shù)據(jù)結(jié)構(gòu)(保存其他數(shù)據(jù)的類型)

2: 結(jié)構(gòu)會自動生成初始化方法
3: 結(jié)構(gòu)中的方法在默認情況下是不允許修改結(jié)構(gòu)中的屬性除非加上mutating關(guān)鍵字

struct Student {
    var name: String
    var age: Int
    
    func study(courseName: String) {
        print("\\(name)正在學(xué)習(xí).")
    }
    mutating func getOlder() {
        age += 1
    }
}

委托

有的時候某個對象要做某件事情但其自身又沒有能力做這件事情
這個時候就可以使用委托回調(diào)的編程模式讓別的對象來做這件事情
以考試找槍手代考為例
實現(xiàn)委托回調(diào)的編程模式有以下幾個步驟:

1 設(shè)計一個協(xié)議(被委托方必須要遵循協(xié)議才能給別的對象當委托)

protocol ExamDelegate: class {

    func answerTheQuestion()
}
class Student {
    var name: String
    weak var delegate: ExamDelegate?//2委托方添加一個屬性其類型是遵循了協(xié)議的被委托方
    init(name: String) {
        self.name = name
    }
    func joinExam() {
        print("姓名: \\(name)")
        delegate?.answerTheQuestion()
    }
}


3自己做不了的事情委托給別的對象來做

class Gunman: ExamDelegate {//4讓類遵循協(xié)議成為被委托方(協(xié)議表能力)
    func answerTheQuestion() {//5 遵循協(xié)議就必須要實現(xiàn)協(xié)議中的方法(協(xié)議表約定)
        print("奮筆疾書各種答案")
    }
}

6它遵循了協(xié)議所以有充當委托的能力也就是說可以扮演被委托方的角色

let stu = LazyStudent(name: "老王")
let gun = Gunman()
stu.delegate = gun
stu.joinExam()

運行結(jié)果

屏幕快照 2016-08-19 下午5.49.57.png

內(nèi)存管理

Swift 是自動管理內(nèi)存的,這也就是說,我們不再需要操心內(nèi)存的申請和分配。當我們通過初始化創(chuàng)建一個對象時,Swift 會替我們管理和分配內(nèi)存。而釋放的原則遵循了自動引用計數(shù) (ARC) 的規(guī)則:當一個對象沒有引用的時候,其內(nèi)存將會被自動回收。這套機制從很大程度上簡化了我們的編碼,我們只需要保證在合適的時候?qū)⒁弥每?(比如超過作用域,或者手動設(shè)為 nil 等)

如果程序中出現(xiàn)了類與類之間雙向關(guān)聯(lián)關(guān)系 必須要將其中一端設(shè)置為weak引用 否則將會形成循環(huán)引用導(dǎo)致ARC無法釋放內(nèi)存

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

員工與部門為例

class Emp {
weak var dept: Dept?//weak 破除循環(huán)
init(dept: Dept) {
        print("創(chuàng)建一個員工")
        self.dept = dept
    }
    deinit { //在銷毀對象的時候調(diào)用
        print("銷毀一個員工")
    }
}

class Dept {
    var manager: Emp?
    
    init() {
        print("創(chuàng)建一個部門")
    }

    deinit {
        print("銷毀一個部門")
    }
}

func bar() {
    let dept = Dept()
    let emp = Emp(dept: dept)
    dept.manager = emp
}

bar()

泛型

泛型 (generic) - 讓類型不再是程序中的硬代碼, Swift中的類、結(jié)構(gòu)和枚舉都可以使用泛型

 泛型限定<T: Comparable>限定T類型必須是遵循了Comparable協(xié)議的類型

定義一個虛擬類型T, 調(diào)用函數(shù)時根據(jù)傳入的參數(shù)類型來決定T到底是什么

func myMin<T: Comparable>(a: T, _ b: T) -> T {
    return a < b ? a : b
}
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: "小強", age: 35)
var stu2 = Student(name: "狒狒", age: 18)

let minStu = myMin (stu1,stu2)
print(minStu.age)

異常處理

定義一個遵循ErrorType協(xié)議的枚舉
通過不同的case定義程序中可能出現(xiàn)的若干種異常狀況

enum FractionError: ErrorType {
    case ZeroDenominator    // 分母為0
    case DivideByZero       // 除以0
}

以分數(shù)為例

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)
}

如果能夠保證代碼不出錯可以在try后面加!
如果不確定代碼是否出錯可以在try后面加?
需要注意的是有?的地方會產(chǎn)生Optional(可空類型)
稍后可能還需要對可空類型進行拆封, 拆封方式有二:

  1. 不安全的做法: xxx!
  2. 安全的做法: 用if let = xxx { }進行拆封

func foo() {
    
    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("出錯了! 我也不知道什么問題")
}
最后編輯于
?著作權(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)容

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