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

字典與集合

  • 寫在前面
    數(shù)組、字典、集合是Swift中最廣泛使用的三種集合類型(Three Primary Collection Type),在上周的總結(jié)中,已經(jīng)分析了數(shù)組類型的內(nèi)容,這里總結(jié)一下字典與集合。

字典

字典也是一種常用的Collection Type,是存放鍵-值對(duì)組合的容器,也就是說,可以通過字典中的鍵,獲取對(duì)應(yīng)的值;字典是可空類型,因?yàn)榻o出的鍵可以沒有相應(yīng)的值與之對(duì)應(yīng),字典的鍵-值表達(dá)形式可以表述為:

key  --> value

創(chuàng)建字典
與數(shù)組類似,創(chuàng)建一個(gè)字典,我們可以用定義常量let以及定義變量var來創(chuàng)建一個(gè)字典,但是,如果使用let定義,我們就不能對(duì)字典中的元素進(jìn)行增、刪、改等操作,只能對(duì)其進(jìn)行遍歷。

var dict: [String: String] = ["abacus": "算盤", "abnormal":"異常的", "hello" :
    "你好", "good": "好的"]

通過打印字典中某個(gè)元素的key,我們可以獲得其value,當(dāng)然,如果沒有對(duì)應(yīng)的key,系統(tǒng)也只會(huì)返回nil。

print(dict["hello"]!)
print(dict["abcxyz"])

// 你好
// nil

增:
在字典中添加元素,我們通常采用的方式是:

dict["shit"] = "雞腿"
dict["delicious"] = "好吃的"
print(dict)

刪:
在字典中刪除元素,我們可以直接使用remove方法:

dict.removeValueForKey("hello")

也可以將某個(gè)元素的值賦值為空:

dict["hello"] = nil

這兩行代碼是完全等效的。

改:
修改元素時(shí),直接將某個(gè)key的value進(jìn)行修改:

dict["shit"] = "牛糞"
print(dict)

查:
與數(shù)組類似,在字典中,我們也可以對(duì)整個(gè)字典進(jìn)行遍歷來查詢字典中所有的元素,我們可以通過元組直接遍歷字典中的鍵和值:

for (key, value) in dict {
    print("\(key) ---> \(value)")
}

當(dāng)然,我們也可以只遍歷字典中所有的鍵:

for key in dict.keys {
    print("\(key) ---> \(dict[key])")
}

也可以只遍歷字典中所有的值:

for value in dict.values {
    print(value)
}

集合

集合是將集合中的元素在磁盤空間中通過生成哈希碼(hash code)散列存放的一種Collection Type。

創(chuàng)建集合
與創(chuàng)建數(shù)組和字典相同,我們可以通過定義常量let及定義變量var來創(chuàng)建一個(gè)集合

var a: Set<Int> = [1 ,2, 3, 1, 2, 5]

在集合中添加元素,我們使用insert方法:

a.insert(100)            // 添加元素

在集合中刪除元素,使用remove方法:

a.remove(2)              // 刪除元素

在Swift中的集合,與我們數(shù)學(xué)上所學(xué)的集合是相同的,所以,我們可以對(duì)集合進(jìn)行運(yùn)算:

var b: Set<Int> = [3, 5, 7, 9, 11]
print(a.intersect(b))   // 交集(a和b都有的元素)
print(a.union(b))       // 并集(a和b的所有元素)
print(a.subtract(b))    // 差集(a有b沒有的元素)
print(a == b)               // 判斷a和b是否相等
print(b.isSubsetOf(a))      // 判斷a是不是b的子集
let c: Set<Int> = [1, 3]
print(a.isSubsetOf(c))      // 判斷c是不是a的子集
print(a.isSupersetOf(c))    // 判斷c是不是a的超集

函數(shù)

函數(shù)(Function)

在程序設(shè)計(jì)中,常將一些常用的功能模塊編寫成函數(shù),放在函數(shù)庫中供公共選用。要善于利用函數(shù),以減少重復(fù)編寫程序段的工作量。

函數(shù)的參數(shù)名
函數(shù)名(外部參數(shù)名 內(nèi)部參數(shù)名:類型,外部參數(shù)名 內(nèi)部參數(shù)名:類型)
如果不寫外部參數(shù)名那么內(nèi)部參數(shù)名也是外部參數(shù)名
可以使用_來作為外部參數(shù)名表示省略外部參數(shù)名

func myMIn(a x: Int, b y: Int) -> Int {
    return x < y ? x : y
}

調(diào)用函數(shù)的時(shí)候要寫函數(shù)的外部參數(shù)名

print(myMIn(a: 3, b: 5))

函數(shù)的定義和調(diào)用
定義函數(shù):
函數(shù)定義的關(guān)鍵字:func
func 函數(shù)名(參數(shù)列表) -> 返回類型 { 函數(shù)的執(zhí)行體 } Swift中函數(shù)的參數(shù)可以設(shè)定默認(rèn)值
如果在調(diào)用函數(shù)的時(shí)候沒有給該參數(shù)賦值就直接使用默認(rèn)值

func sayHello(personName: String, _ alreadyGreeted: Bool = false) -> String {
     let greeting = "Hello," + personName + "!"
     如果函數(shù)的返回類型不是void 那么函數(shù)中一定有return語句
     return greeting
     person = "王小錘"      // 編譯錯(cuò)誤
    if alreadyGreeted {
        return "怎么又是你," + personName + "!"
    }
    else {
        return "你好," + personName + "!"
    }
}

調(diào)用函數(shù)
函數(shù)名(參數(shù)值)
調(diào)用Swift的函數(shù)時(shí),在默認(rèn)情況下從第二個(gè)參數(shù)開始需要寫函數(shù)名
如果沒有給第二個(gè)參數(shù)賦值那么就直接使用默認(rèn)值

print(sayHello("王大錘", true))
et str = sayHello("逗比")
print(str)

在Swift中,函數(shù)的參數(shù)列表是可變參數(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(90, 82, 37, 68, 55, 11, 99)

我們也可以使用元組(tuple)讓函數(shù)一次性返回多條數(shù)據(jù)

func minMax(array: [Int]) -> (min: Int, max: Int) {
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        }
        else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}

let b = minMax([23, 45, 99, 68, 72, 12, 55])
    print(b.min)        // print(b.0)
    print(b.max)        // print(b.1)

func swap(inout x: Int, inout _ y: Int) -> Void {
    // (x, y) = (y, x)
    let temp = x
    x = y
    y = temp      // 兩種寫法等效
}

函數(shù)調(diào)用傳參都是傳值

swap(&x, &y)
print("x = \(x)")
print("y = \(y)")

inout - 輸入輸出函數(shù)(不僅將數(shù)據(jù)傳入函數(shù)還要從函數(shù)中取出數(shù)據(jù))

func createX(inout x: Int) {
    x = 1000
}

var x = 1
// inout類型的參數(shù)前要加上&符號(hào)
createX(&x)
print(x)

函數(shù)的遞歸調(diào)用(一個(gè)函數(shù)直接或間接的調(diào)用自身)

遞歸的條件:

  1. 遞歸公式
  2. 收斂條件
func f(n: Int) -> Double {
    if n == 0 || n == 1 {
        return 1
    }
    return Double(n) * f(n - 1)
}

遞歸運(yùn)算中的一個(gè)經(jīng)典例題----漢諾伊塔(hannoi)

var counter = 1
func hannoi(n: Int, _ a: String, _ b: String, _ c:String) {
    if n > 0 {
    hannoi(n - 1, a, c, b)
    print("\(counter): \(a) --> \(b)")
    counter += 1
    hannoi(n - 1, c, b, a)
    }
}
hannoi(5, "A", "B", "C")

函數(shù)的閉包

在Swift中,函數(shù)是一種類型,這也就意味著函數(shù)可以作為變量或常量的類型,同理函數(shù)也可以作為另一個(gè)函數(shù)的參數(shù)或返回值:

func foo (array:[Int], fn: (Int , Int) -> Int) -> Int {
    var sum = array[0]
    for x in array[1..<array.count] {
        sum = fn(sum, x)
    }
    return sum
}
let a = [1, 2, 3, 4, 5]

當(dāng)調(diào)用foo函數(shù)時(shí)第二個(gè)參數(shù)可以傳什么?

  1. 所有自定義的(Int, Int) -> Int類型的函數(shù)
print(foo(a, fn: sum))
  1. 傳入二元運(yùn)算符: +-*/%(因?yàn)檫\(yùn)算符也是函數(shù))
print(foo(a, fn:  +))
  1. 傳入匿名函數(shù)(閉包)
    3.1 完整的閉包寫法
print(foo(a, fn: { (a: Int, b: Int) -> Int in
    return a + b
}))

3.2 省略掉類型和不必要的括號(hào)

print(foo(a, fn: { a, b  in a + b }))

3.3 省略參數(shù)名

print(foo(a, fn: { $0 + $1 }))

3.4 尾隨閉包

print(foo(a) { (a, b) -> Int in
    return a + b
})
print(foo(a) { $0 + $1 })

如果函數(shù)的最后一個(gè)參數(shù)是閉包可以寫成尾隨閉包的形式,也就是將閉包放到函數(shù)參數(shù)的圓括號(hào)外面寫在一對(duì)花括號(hào)中
,如果函數(shù)后面有尾隨閉包且函數(shù)的圓括號(hào)中沒有參數(shù)
那么函數(shù)的圓括號(hào)也可以省略(僅限于有尾隨閉包的場(chǎng)景)。

var array = ["game", "abacus", "hello", "cat", "good", "internationalization", "chaos", "dislike", "zealot", "young"]
array.sortInPlace(>)
array.sortInPlace({ $0 > $1})
array.sortInPlace() { $0 > $1}
array.sortInPlace { $0 > $1 }
array.sortInPlace {
 if $0.characters.count == $1.characters.count {
        return $0 < $1
    }
    return $0.characters.count < $1.characters.count
}
print(array)

在數(shù)組中,我們常常會(huì)遇到一些我們不需要的元素,這個(gè)時(shí)候,我們就需要一些方法,來挑選出我們需要的元素

1.過濾
過濾方法filter,將我們不需要的元素過濾出去,留下需要的元素,例如:

let array = [23, 37, 96, 55, 40, 92, 68, 88]

我們要從數(shù)組中挑選出所有大于50的元素:

let newArray = array.filter { $0 > 50 }
print(newArray)

或者從數(shù)組中過濾出所有的偶數(shù):

let newArray2 = array.filter { $0 % 2 == 0 }
print(newArray2)

2.映射
在數(shù)學(xué)里,映射則是個(gè)術(shù)語,指兩個(gè)元素的集之間元素相互對(duì)應(yīng)的關(guān)系,為名詞;亦指形成對(duì)應(yīng)關(guān)系”這一個(gè)動(dòng)作,動(dòng)詞

let newArray3 = array.map { $0 * $0 }
print(newArray3)

let oArray = array.map { (x: Int) -> Int in
    return x / 2
}

3.縮減(歸約)
將數(shù)組中所有元素求和:

let result1 = array.reduce(0, combine: +)
print(result1)

將數(shù)組中所有元素求積:

let result2 = array.reduce(1, combine: *)
print(result2)

其中,"0"和"1"代表賦的初始值,combine表示縮減的方法。
選出數(shù)組中最大的數(shù):

let result3 = array.reduce(array[0]) {
    $1 > $0 ? $1 : $0
}
print(result3)

面向?qū)ο蟪绦蛟O(shè)計(jì)

類(class)

類是面向?qū)ο蟪绦蛟O(shè)計(jì)中的概念,是面向?qū)ο缶幊痰幕A(chǔ)。類的實(shí)質(zhì)是一種數(shù)據(jù)類型,類似于int、char等基本類型,不同的是它是一種復(fù)雜的數(shù)據(jù)類型。因?yàn)樗谋举|(zhì)是類型,而不是數(shù)據(jù),所以不存在于內(nèi)存中,不能被直接操作,只有被實(shí)例化為對(duì)象時(shí),才會(huì)變得可操作。類是對(duì)現(xiàn)實(shí)生活中一類具有共同特征的事物的抽象。如果一個(gè)程序里提供的類型與應(yīng)用中的概念有直接的對(duì)應(yīng),這個(gè)程序就會(huì)更容易理解,也更容易修改。一組經(jīng)過很好選擇的用戶定義的類會(huì)使程序更簡(jiǎn)潔。

類的內(nèi)部封裝了方法,用于操作自身的成員。類是對(duì)某種對(duì)象的定義,具有行為(be-havior),它描述一個(gè)對(duì)象能夠做什么以及做的方法(method),它們是可以對(duì)這個(gè)對(duì)象進(jìn)行操作的程序和過程。它包含有關(guān)對(duì)象動(dòng)作方式的信息,包括它的名稱、方法、屬性和事件。

創(chuàng)建類
創(chuàng)建一個(gè)類,我們一般分為三步:
步驟1: 定義類(如果你要用的類蘋果已經(jīng)提供了就直接進(jìn)入第二步)
定義類就可以創(chuàng)建出新的類型
例如,我么定義一個(gè)學(xué)生類

enum Gender {               // 枚舉
    case Male, Female
}

class Student {
    // 變量定義到類的外面就叫變量 - variable
    // 變量定義到類的里面就叫屬性 - property
    // 數(shù)據(jù)抽象 - 找到和學(xué)生相關(guān)的屬性(找名詞)
    var name: String
    var age: Int
    
    // 初始化方法(構(gòu)造方法/構(gòu)造器) - constructor
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    // 函數(shù)寫到類的外面就叫函數(shù) - function
    // 函數(shù)寫到類的里面就叫方法 - method
    // 行為抽象 - 找到和學(xué)生相關(guān)的方法(找動(dòng)詞)
    func eat() {
        print("\(name)正在吃飯.")
    }
    func study(courseName: String) {
        print("\(name)正在學(xué)習(xí)\(courseName).")
    }
    func watchJapaneseAV() {
        if age >= 18 {
            print("\(name)正在觀看島國(guó)愛情動(dòng)作片.")
        }
        else {
            print("親愛的\(name),我們推薦你觀看《熊出沒》.")
        }
    }
}

步驟2: 創(chuàng)建對(duì)象(調(diào)用初始化方法)

let stu1 = Student(name: "王尼瑪", age: 30)

步驟3: 給對(duì)象發(fā)消息(通過給對(duì)象發(fā)消息來解決問題)

stu1.eat()
stu1.study("Swift程序設(shè)計(jì)")
stu1.watchJapaneseAV()

let stu2 = Student(name: "王大錘", age: 15)
stu2.eat()
stu2.study("中國(guó)近代史")
stu2.watchJapaneseAV()

初始化方法重載
在一個(gè)類中,我們可以定義多個(gè)初始化方法,這叫做初始化方法的重載:

public class Point {
    var x: Double
    var y: Double

    convenience init() {
        self.init(x: 0, y: 0)
    }
    
    convenience init(point: (Double, Double)) {
        self.init(x: point.0, y: point.1)
    }
    
 
    init(x: Double, y: Double) {
        self.x = x
        self.y = y
    }
    
    func moveTO(x: Double, _ y: Double) {
        self.x = x
        self.y = y
    }
    
    func distanceTo(other: Point) -> Double {
        let dx = x - other.x
        let dy = y - other.y
        return sqrt(dx * dx + dy * dy)
    }
}

convenience重載過的初始化方法,我們叫做"便利初始化方法",也叫"便利構(gòu)造器";而被調(diào)用的初始化方法,我們叫做"指派初始化方法",也叫"指派構(gòu)造器"。

訪問修飾符
在創(chuàng)建一個(gè)類時(shí),我們對(duì)用處不同的類可以添加不同的訪問修飾符來進(jìn)行區(qū)別,其中:

  • public (公開)
  • internal (內(nèi)部的) - 默認(rèn)
  • private (私有)
    public表示本以被外部調(diào)用;
    internal表示只能在本項(xiàng)目?jī)?nèi)使用;
    private表示只能讀取,不能更改。

存儲(chǔ)屬性通常是private的 因?yàn)閿?shù)據(jù)要保護(hù)起來
方法一般是public的 因?yàn)榉椒ㄊ菍?duì)象接受的消息
如果自定義的類沒有打算在其他項(xiàng)目中使用 可以不寫訪問修飾符
直接使用默認(rèn)的internal修飾符表示在本項(xiàng)目中公開對(duì)其他項(xiàng)目私有

計(jì)算屬性
在類的方法中,有一些屬性是通過對(duì)定義的屬性做運(yùn)算得到的屬性,但又不能成為方法,這種屬性我們叫做計(jì)算屬性,而被運(yùn)算的屬性叫做存儲(chǔ)屬性。

public class Circle {
    // stored property
    // 存儲(chǔ)屬性(保存和圓相關(guān)的數(shù)據(jù)的屬性)
    var center: Point
    var radius: Double
    
    init(center: Point, radius: Double) {
        self.radius = radius
        self.center = center
    }
    
    // 通常獲得某個(gè)計(jì)算出的值的方法都可以設(shè)計(jì)成計(jì)算屬性
    // computational property
    // 計(jì)算屬性(通過對(duì)存儲(chǔ)屬性做運(yùn)算得到的屬性)
    var perimeter: Double {
        // 圓的周長(zhǎng)是一個(gè)只讀屬性
        // 所以此處只有g(shù)et{}沒有set{}
        get { return 2 * M_PI * radius }
    }
    
    var area: Double {
        get { return M_PI * radius * radius }
    }
}

繼承和多態(tài)

繼承
從已有的類創(chuàng)建新類的過程提供繼承信息的稱為父類(超類/基類)
得到繼承信息的稱為子類(派生類/衍生類)
通常子類除了得到父類的繼承信息還會(huì)增加一些自己特有的東西
所以子類的能力一定比父類更強(qiáng)大
繼承的意義在于子類可以服用父類的代碼并且增強(qiáng)系統(tǒng)現(xiàn)有的功能

創(chuàng)建一個(gè)叫"人"的類

 import Foundation

enum Gender {
    case Male
    case Female
}

class Person {
    var name: String
    var age: Int
    var gender: Gender
   
    init(name: String, age: Int, gender: Gender) {
        self.name = name
        self.age = age
        self.gender = gender
    }
    
    func eat() {
        print("\(name)正在吃飯")
    }    
}

在"人"這個(gè)類的基礎(chǔ)上,創(chuàng)建一個(gè)叫"老師"的子類:

class Teacher: Person {
    var title: String
    
    
    init(name: String, age: Int, gender: Gender, title: String) {
        self.title = title
        super.init(name: name, age: age, gender: gender )
    }
    
    
    func teach(courseName: String) {
        print("\(name)\(title)正在教\(courseName).")
    }

}

我們可以將子類型的對(duì)象賦值給父類型的變量(因?yàn)樽宇愋透割愔g是IS-A關(guān)系)
學(xué)生是人,老師是人,所以學(xué)生和老師的對(duì)象可以賦值給人類型的變量

let p2: Person = Student(name: "王尼瑪", age: 18, gender: .Female, major: "計(jì)算機(jī)科學(xué)與技術(shù)")
p2.eat()

在繼承中,我們經(jīng)常會(huì)遇到類型轉(zhuǎn)換的問題
如果要將父類型的變量轉(zhuǎn)換成子類型需要用as運(yùn)算股進(jìn)行類型轉(zhuǎn)換
如果能夠確認(rèn)父類型的變量中就是某種子類型的的對(duì)象可以用as!進(jìn)行轉(zhuǎn)換
如果不能確定父類型的變量中是哪種子類型可以用as?嘗試轉(zhuǎn)換

(p2 as! Student).study("Swift程序設(shè)計(jì)")
if let temp = p2 as? Teacher {
    temp.teach("Java")
}
else {
    print("\(p2.name)不是老師")
}

多態(tài)
對(duì)于多態(tài),我們先上代碼:
定義一個(gè)叫"寵物"的父類:

enum Gender {
    case Male
    case Female
}

class Pet {
    var nickname: String
    var gender: Gender
    var age: Int
    
    init(nickname: String, gender: Gender, age: Int) {
        self.nickname = nickname
        self.gender = gender
        self.age = age
    }
    
    func eat() {
        print("\(nickname)正在吃東西.")
    }
    
    func play() {
        print("\(nickname)正在玩耍.")
    }
    
    func shout() {
        print("\(nickname)發(fā)出了叫聲.")
    }
}

在"寵物"類的基礎(chǔ)上,定義一個(gè)"貓"類,一個(gè)"狗"類

class Cat: Pet {
    var hairColor: String?
    
    override func play() {
        super.play()
        print("\(nickname)正在玩毛線球.")
    }
    
    override func shout() {
        print("\(nickname):喵喵喵!")
    }
    
    func catchTheMouse() {
        print("\(nickname)正在抓老鼠.")
    }
}

父類有的方法子類可以重新實(shí)現(xiàn) 這個(gè)過程叫做方法重寫
需要在方法前添加override關(guān)鍵字
重寫有時(shí)也被稱為置換/覆蓋/復(fù)寫

我們創(chuàng)建一個(gè)對(duì)象數(shù)組:

let petsArray = [
    Cat(nickname: "加菲", gender: .Female, age: 2),
    Dog(nickname: "旺財(cái)", gender: .Male, age: 3, isLarge: true)

調(diào)用相同的方法:

for pet in petsArray {
    pet.eat()
    pet.play()
}

但是最后運(yùn)行代碼的時(shí)候會(huì)發(fā)現(xiàn),調(diào)用了相同的方法,得到的結(jié)果卻完全不同。

同樣的對(duì)象類型(Pet類型)接收相同的消息(調(diào)用相同的方法)
但是做了不同的事情 這就是多態(tài)(polymrophism)

實(shí)現(xiàn)多態(tài)的關(guān)鍵步驟:

  1. 方法重寫(子類在繼承父類的過程中對(duì)父類已有的方法進(jìn)行重寫,而且不同的子類給出各自不同的實(shí)現(xiàn)版本)
  2. 對(duì)象造型(將子類對(duì)象當(dāng)成父類來使用)
    可以通過if+as?將父類型安全的轉(zhuǎn)換成子類型談后調(diào)用子類特有方法
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容