swift4.0學(xué)習(xí)筆記

1

swift是類型安全語(yǔ)言
1、在 Swift 中,所有的基本類型:整數(shù)(Integer)、浮點(diǎn)數(shù)(floating-point)、布爾值(Boolean)、字符串(string)、數(shù)組(array)和字典(dictionary),都是值類型,并且在底層都是以結(jié)構(gòu)體的形式所實(shí)現(xiàn)。
值類型被賦予給一個(gè)變量、常量或者被傳遞給一個(gè)函數(shù)的時(shí)候,其值會(huì)被拷貝。
在 Swift 中,所有的結(jié)構(gòu)體和枚舉類型都是值類型。這意味著它們的實(shí)例,以及實(shí)例中所包含的任何值類型屬性,在代碼中傳遞的時(shí)候都會(huì)被復(fù)制。
引用類型指向內(nèi)存地址,于此相反,與值類型不同,引用類型在被賦予到一個(gè)變量、常量或者被傳遞到一個(gè)函數(shù)時(shí),其值不會(huì)被拷貝。因此,引用的是已存在的實(shí)例本身而不是其拷貝。

2、“等價(jià)于”表示兩個(gè)類類型(class type)的常量或者變量引用同一個(gè)類實(shí)例。
“等于”表示兩個(gè)實(shí)例的值“相等”或“相同”,判定時(shí)要遵照設(shè)計(jì)者定義的評(píng)判標(biāo)準(zhǔn),因此相對(duì)于“相等”來說,這是一種更加合適的叫法。

3、結(jié)構(gòu)體(struct)屬于值類型。當(dāng)值類型的實(shí)例被聲明為常量的時(shí)候,它的所有屬性也就成了常量。

屬于引用類型的類(class)則不一樣。把一個(gè)引用類型的實(shí)例賦給一個(gè)常量后,仍然可以修改該實(shí)例的變量屬性。

2.可選值

Swift 的可選類型可以讓你暗示任意類型的值缺失,并不需要一個(gè)特殊值,

Swift 的 Int 類型有一種構(gòu)造器,作用是將一個(gè) String 值轉(zhuǎn)換成一個(gè) Int 值。然而,并不是所有的字符串都可以轉(zhuǎn)換成一個(gè)整數(shù)。字符串 "123" 可以被轉(zhuǎn)換成數(shù)字 123 ,但是字符串 "hello, world" 不行。
下面的例子使用這種構(gòu)造器來嘗試將一個(gè) String 轉(zhuǎn)換成 Int:

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber 被推測(cè)為類型 "Int?", 或者類型 "optional Int"

因?yàn)樵摌?gòu)造器可能會(huì)失敗,所以它返回一個(gè)可選類型(optional)Int,而不是一個(gè) Int。一個(gè)可選的 Int 被寫作 Int? 而不是 Int。問號(hào)暗示包含的值是可選類型,也就是說可能包含 Int 值也可能不包含值。(不能包含其他任何值比如 Bool 值或者 String 值。只能是 Int 或者什么都沒有。)

nil不能用于非可選的常量和變量。如果你的代碼中有常量或者變量需要處理值缺失的情況,請(qǐng)把它們聲明成對(duì)應(yīng)的可選類型。

Swift 的 nil 和 Objective-C 中的 nil 并不一樣。在 Objective-C 中,nil 是一個(gè)指向不存在對(duì)象的指針。在 Swift 中,nil 不是指針——它是一個(gè)確定的值,用來表示值缺失。任何類型的可選狀態(tài)都可以被設(shè)置為 nil,不只是對(duì)象類型。
空合運(yùn)算符(a ?? b)將對(duì)可選類型 a 進(jìn)行空判斷,如果 a 包含一個(gè)值就進(jìn)行解封,否則就返回一個(gè)默認(rèn)值 b。表達(dá)式 a 必須是 Optional 類型。默認(rèn)值 b 的類型必須要和 a 存儲(chǔ)值的類型保持一致。
空合運(yùn)算符是對(duì)以下代碼的簡(jiǎn)短表達(dá)方法:
a != nil ? a! : b

3

在 if 條件語(yǔ)句中使用常量和變量來創(chuàng)建一個(gè)可選綁定,僅在 if 語(yǔ)句的句中(body)中才能獲取到值。相反,在 guard 語(yǔ)句中使用常量和變量來創(chuàng)建一個(gè)可選綁定,僅在 guard 語(yǔ)句外且在語(yǔ)句后才能獲取到值,請(qǐng)參考提前退出

if let constantName = someOptional {
    statements
}

當(dāng)你確定可選類型確實(shí)包含值之后,你可以在可選的名字后面加一個(gè)感嘆號(hào)(!)來獲取值。這個(gè)驚嘆號(hào)表示“我知道這個(gè)可選有值,請(qǐng)使用它?!边@被稱為可選值的強(qiáng)制解析

隱式解析可選類型

第一次被賦值之后,可以確定一個(gè)可選類型總會(huì)有值。在這種情況下,每次都要判斷和解析可選值是非常低效的,因?yàn)榭梢源_定它總會(huì)有值。這種類型的可選狀態(tài)被定義為隱式解析可選類型(implicitly unwrapped optionals)
一個(gè)隱式解析可選類型其實(shí)就是一個(gè)普通的可選類型,但是可以被當(dāng)做非可選類型來使用

let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要感嘆號(hào)來獲取值
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString  // 不需要感嘆號(hào)

4.

字符串字面值使用雙影號(hào)需要轉(zhuǎn)義符

let zhuayi = "我是都\"放假\"放假啊"
print(zhuayi)

由于多行字符串字面量使用了三個(gè)雙引號(hào),而不是一個(gè),所以你可以在多行字符串字面量里直接使用雙引號(hào)(")而不必加上轉(zhuǎn)義符(\)。要在多行字符串字面量中使用 """ 的話,就需要使用至少一個(gè)轉(zhuǎn)義符(\)

let threeDoubleQuotes = """
Escaping the first quote \"""
Escaping all three quotes \"\"\"
"""

5

swift中使用for循環(huán),接受的(character)不需要聲明應(yīng)該默認(rèn)是常量(let character: Character),類型自己推斷

emptyStr = "uuututututut"
for character in emptyStr {
    print(character)
}

//和數(shù)組一樣,我們?cè)谟米值渥置媪繕?gòu)造字典時(shí),如果它的鍵和值都有各自一致的類型,那么就不必寫出字典的類型。 airports字典也可以用這種簡(jiǎn)短方式定義:
var hgg = ["ghg":"fffg","ddffa":"dfasf"]

6

不像 C 語(yǔ)言,Swift 允許多個(gè) case 匹配同一個(gè)值。實(shí)際上,在這個(gè)例子中,點(diǎn)(0, 0)可以匹配所有四個(gè) case。但是,如果存在多個(gè)匹配,那么只會(huì)執(zhí)行第一個(gè)被匹配到的 case 分支。考慮點(diǎn)(0, 0)會(huì)首先匹配case (0, 0),因此剩下的能夠匹配的分支都會(huì)被忽視掉。

7

嚴(yán)格上來說,雖然沒有返回值被定義,greet(person:) 函數(shù)依然返回了值。沒有定義返回類型的函數(shù)會(huì)返回一個(gè)特殊的Void值。它其實(shí)是一個(gè)空的元組(tuple),沒有任何元素,可以寫成()。

func greet(person: String) {
    print("Hello, \(person)!")
}
greet(person: "Dave")
// 打印 "Hello, Dave!"

9

如果函數(shù)返回的元組類型有可能整個(gè)元組都“沒有值”,你可以使用可選的( optional ) 元組返回類型反映整個(gè)元組可以是nil的事實(shí)。你可以通過在元組類型的右括號(hào)后放置一個(gè)問號(hào)來定義一個(gè)可選元組,例如 (Int, Int)? 或 (String, Int, Bool)?

注意 可選元組類型如 (Int, Int)? 與元組包含可選類型如 (Int?, Int?) 是不同的.可選的元組類型,整個(gè)元組是可選的,而不只是元組中的每個(gè)元素值。

10

每個(gè)函數(shù)都有種特定的函數(shù)類型,函數(shù)的類型由函數(shù)的參數(shù)類型和返回類型組成,例(Int, Int) -> Int。無返回值得函數(shù)其實(shí)是返回void ,() -> Void

11

每個(gè)函數(shù)參數(shù)都有一個(gè)參數(shù)標(biāo)簽( argument label )以及一個(gè)參數(shù)名稱( parameter name )。參數(shù)標(biāo)簽在調(diào)用函數(shù)的時(shí)候使用;調(diào)用的時(shí)候需要將函數(shù)的參數(shù)標(biāo)簽寫在對(duì)應(yīng)的參數(shù)前面。參數(shù)名稱在函數(shù)的實(shí)現(xiàn)中使用。默認(rèn)情況下,函數(shù)參數(shù)使用參數(shù)名稱來作為它們的參數(shù)標(biāo)簽。

你可以在參數(shù)名稱前指定它的參數(shù)標(biāo)簽,中間以空格分隔:

func someFunction(argumentLabel parameterName: Int) {
    // 在函數(shù)體內(nèi),parameterName 代表參數(shù)值
}

如果你不希望為某個(gè)參數(shù)添加一個(gè)標(biāo)簽,可以使用一個(gè)下劃線(_)來代替一個(gè)明確的參數(shù)標(biāo)簽。
函數(shù)參數(shù)默認(rèn)是常量。試圖在函數(shù)體中更改參數(shù)值將會(huì)導(dǎo)致編譯錯(cuò)誤(compile-time error)。這意味著你不能錯(cuò)誤地更改參數(shù)值。如果你想要一個(gè)函數(shù)可以修改參數(shù)的值,并且想要在這些修改在函數(shù)調(diào)用結(jié)束后仍然存在,那么就應(yīng)該把這個(gè)參數(shù)定義為輸入輸出參數(shù)(In-Out Parameters)

12

對(duì)于字符串中的字符來說,“大于”表示“按照字母順序較晚出現(xiàn)

func backward(_ s1:String,_ s2: String) -> Bool {
    return s1 > s2
}

提前退出

像if語(yǔ)句一樣,guard的執(zhí)行取決于一個(gè)表達(dá)式的布爾值。我們可以使用guard語(yǔ)句來要求條件必須為真時(shí),以執(zhí)行g(shù)uard語(yǔ)句后的代碼。不同于if語(yǔ)句,一個(gè)guard語(yǔ)句總是有一個(gè)else從句,如果條件不為真則執(zhí)行else從句中的代碼。
如果guard語(yǔ)句的條件被滿足,則繼續(xù)執(zhí)行g(shù)uard語(yǔ)句大括號(hào)后的代碼。將變量或者常量的可選綁定作為guard語(yǔ)句的條件,都可以保護(hù)guard語(yǔ)句后面的代碼。
如果條件不被滿足,在else分支上的代碼就會(huì)被執(zhí)行。這個(gè)分支必須轉(zhuǎn)移控制以退出guard語(yǔ)句出現(xiàn)的代碼段。它可以用控制轉(zhuǎn)移語(yǔ)句如return,break,continue或者throw做這件事,或者調(diào)用一個(gè)不返回的方法或函數(shù),例如fatalError()。

13

閉包是自包含的函數(shù)代碼塊,可以在代碼中被傳遞和使用

閉包的函數(shù)體部分由關(guān)鍵字in引入。該關(guān)鍵字表示閉包的參數(shù)和返回值類型定義已經(jīng)完成,閉包函數(shù)體即將開始。

{ (parameters) -> returnType in
    statements
}
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
    return s1 > s2
})

尾隨閉包

閉包表達(dá)式語(yǔ)法一節(jié)中作為 sorted(by:) 方法參數(shù)的字符串排序閉包可以改寫為:

reversedNames = names.sorted() { $0 > $1 }

如果閉包表達(dá)式是函數(shù)或方法的唯一參數(shù),則當(dāng)你使用尾隨閉包時(shí),你甚至可以把 () 省略掉:

reversedNames = names.sorted { $0 > $1 }

值捕獲

func makeIncrementer(forIncrement amount: Int) -> () -> Int {
        var runingTotal = 0
        func incrementre() -> Int {
            runingTotal += amount
            return runingTotal
        }
        return incrementre
    }

內(nèi)部函數(shù)incrementre可以捕獲外部變量runingTotal,amount,捕獲引用保證了 runningTotal 和 amount 變量在調(diào)用完 makeIncrementer 后不會(huì)消失,并且保證了在下一次執(zhí)行 incrementer 函數(shù)時(shí),runningTotal 依舊存在。

let incrementByTen = makeIncrementer(forIncrement: 10)
        incrementByTen()//返回10
        incrementByTen()//返回20

makeIncrementer第一次調(diào)用這個(gè)函數(shù)并不會(huì)執(zhí)行incrementre這個(gè)函數(shù),因?yàn)樗麤]有調(diào)用
當(dāng)調(diào)用了incrementByTen()才會(huì)執(zhí)行incrementre函數(shù)

函數(shù)和閉包都是引用類型,類是引用類型
結(jié)構(gòu)體和枚舉是值類型

14 枚舉關(guān)聯(lián)值和原始值

原始值和關(guān)聯(lián)值是不同的。原始值是在定義枚舉時(shí)被預(yù)先填充的值,像上述三個(gè) ASCII 碼。對(duì)于一個(gè)特定的枚舉成員,它的原始值始終不變。關(guān)聯(lián)值是創(chuàng)建一個(gè)基于枚舉成員的常量或變量時(shí)才設(shè)置的值,枚舉成員的關(guān)聯(lián)值可以變化。

15 值類型

值類型被賦予給一個(gè)變量、常量或者被傳遞給一個(gè)函數(shù)的時(shí)候,其值會(huì)被拷貝。

16 值類型和引用類型

我們已經(jīng)大量使用了值類型。實(shí)際上,在 Swift 中,所有的基本類型:整數(shù)(Integer)、浮點(diǎn)數(shù)(floating-point)、布爾值(Boolean)、字符串(string)、數(shù)組(array)和字典(dictionary),都是值類型,并且在底層都是以結(jié)構(gòu)體的形式所實(shí)現(xiàn)。

在 Swift 中,所有的結(jié)構(gòu)體和枚舉類型都是值類型。這意味著它們的實(shí)例,以及實(shí)例中所包含的任何值類型屬性,在代碼中傳遞的時(shí)候都會(huì)被復(fù)制。

與值類型不同,引用類型在被賦予到一個(gè)變量、常量或者被傳遞到一個(gè)函數(shù)時(shí),其值不會(huì)被拷貝。因此,引用的是已存在的實(shí)例本身而不是其拷貝。

17

等價(jià)于(===)
不等價(jià)于(!==)
運(yùn)用這兩個(gè)運(yùn)算符檢測(cè)兩個(gè)常量或者變量是否引用同一個(gè)實(shí)例:

if tenEighty === alsoTenEighty {
    print("tenEighty and alsoTenEighty refer to the same Resolution instance.")
}

請(qǐng)注意,“等價(jià)于”(用三個(gè)等號(hào)表示,===)與“等于”(用兩個(gè)等號(hào)表示,==)的不同:

“等價(jià)于”表示兩個(gè)類類型(class type)的常量或者變量引用同一個(gè)類實(shí)例。
“等于”表示兩個(gè)實(shí)例的值“相等”或“相同”,判定時(shí)要遵照設(shè)計(jì)者定義的評(píng)判標(biāo)準(zhǔn),因此相對(duì)于“相等”來說,這是一種更加合適的叫法。

延遲存儲(chǔ)屬性

必須將延遲存儲(chǔ)屬性聲明成變量(使用 var 關(guān)鍵字),因?yàn)閷傩缘某跏贾悼赡茉趯?shí)例構(gòu)造完成之后才會(huì)得到。而常量屬性在構(gòu)造過程完成之前必須要有初始值,因此無法聲明成延遲屬性。

計(jì)算屬性

計(jì)算屬性:根據(jù)構(gòu)造時(shí)傳入的值返回計(jì)算屬性的值
這里只寫了get方法,所以不能更改center的值

struct Point {
    var x = 0.0, y = 0.0
}
struct Size {
    var width = 0.0
    var height = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = origin.x + (size.width/2)
            let centerY = origin.y + (size.height/2)
            return Point(x:centerX,y:centerY)
        }
    }
}
var square = Rect(origin:Point(x: 0.0,y: 0.0),size:Size(width: 10,height: 10))
let initialSquare = square.center
print("\(initialSquare.x),\(initialSquare.y)")
//square.center = Point(x: 15,y: 15)

提供一個(gè)set方法就可以更改值了

struct Rect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set(newCenter) {
            origin.x = newCenter.x - (size.width / 2)
            origin.y = newCenter.y - (size.height / 2)
        }
    }
}

15

實(shí)例屬性屬于一個(gè)特定類型的實(shí)例,每創(chuàng)建一個(gè)實(shí)例,實(shí)例都擁有屬于自己的一套屬性值,實(shí)例之間的屬性相互獨(dú)立。

也可以為類型本身定義屬性,無論創(chuàng)建了多少個(gè)該類型的實(shí)例,這些屬性都只有唯一一份。這種屬性就是類型屬性。

17

結(jié)構(gòu)體和枚舉能夠定義方法是 Swift 與 C/Objective-C 的主要區(qū)別之一。在 Objective-C 中,類是唯一能定義方法的類型。但在 Swift 中,你不僅能選擇是否要定義一個(gè)類/結(jié)構(gòu)體/枚舉,還能靈活地在你創(chuàng)建的類型(類/結(jié)構(gòu)體/枚舉)上定義方法。

18

swift運(yùn)算符兩邊必須有空格


image.png

19

實(shí)例方法是被某個(gè)類型的實(shí)例調(diào)用的方法。你也可以定義在類型本身上調(diào)用的方法,這種方法就叫做類型方法。在方法的func關(guān)鍵字之前加上關(guān)鍵字static,來指定類型方法,類還可以用關(guān)鍵字class來允許子類重寫父類的方法實(shí)現(xiàn)。

20

不論何時(shí),只要在一個(gè)方法中使用一個(gè)已知的屬性或者方法名稱,如果你沒有明確地寫self,Swift 假定你是指當(dāng)前實(shí)例的屬性或者方法。這種假定在上面的Counter中已經(jīng)示范了:Counter中的三個(gè)實(shí)例方法中都使用的是count(而不是self.count)。

使用這條規(guī)則的主要場(chǎng)景是實(shí)例方法的某個(gè)參數(shù)名稱與實(shí)例的某個(gè)屬性名稱相同的時(shí)候。在這種情況下,參數(shù)名稱享有優(yōu)先權(quán),并且在引用屬性時(shí)必須使用一種更嚴(yán)格的方式。這時(shí)你可以使用self屬性來區(qū)分參數(shù)名稱和屬性名稱。

21

使用枚舉成員的rawValue屬性可以訪問該枚舉成員的原始值:

22

你在重寫一個(gè)屬性時(shí),必需將它的名字和類型都寫出來。這樣才能使編譯器去檢查你重寫的屬性是與超類中同名同類型的屬性相匹配的

23

1.setter,getter寫法
class Jia {
    var center: Int  {
        get {
            return 3
        }
        set(newCenter) {
            print("tttt")
        }
    }
}
2.屬性觀察器的寫法
class StepCounter {
    var totalStep:Int = 0 {
        willSet(newTotalStep) {
            print("about to set \(newTotalStep)")//隨便寫
        }
        didSet {
            if totalStep > oldValue {
                print("\(totalStep,oldValue)")
            }
        }
    }
}
3.只讀計(jì)算屬性的聲明可以去掉 get 關(guān)鍵字和花括號(hào):
struct Cuboid {
    var width = 0.0, height = 0.0, depth = 0.0
    var volume: Double {
        return width * height * depth
    }
}

willSet 觀察器會(huì)將新的屬性值作為常量參數(shù)傳入,在 willSet 的實(shí)現(xiàn)代碼中可以為這個(gè)參數(shù)指定一個(gè)名稱,如果不指定則參數(shù)仍然可用,這時(shí)使用默認(rèn)名稱 newValue 表示。

同樣,didSet 觀察器會(huì)將舊的屬性值作為參數(shù)傳入,可以為該參數(shù)命名或者使用默認(rèn)參數(shù)名 oldValue。如果在 didSet 方法中再次對(duì)該屬性賦值,那么新值會(huì)覆蓋舊的值。

你不可以同時(shí)提供重寫的 setter 和重寫的屬性觀察器。如果你想觀察屬性值的變化,并且你已經(jīng)為那個(gè)屬性提供了定制的 setter,那么你在 setter 中就可以觀察到任何值變化了。

23

類和結(jié)構(gòu)體在創(chuàng)建實(shí)例時(shí),必須為所有存儲(chǔ)型屬性設(shè)置合適的初始值,存儲(chǔ)型屬性的值不能處于一個(gè)未知的狀態(tài)。你可以在構(gòu)造器中為存儲(chǔ)型屬性賦初值,也可以在定義屬性時(shí)為其設(shè)置默認(rèn)值

當(dāng)你為存儲(chǔ)型屬性設(shè)置默認(rèn)值或者在構(gòu)造器中為其賦值時(shí),它們的值是被直接設(shè)置的,不會(huì)觸發(fā)任何屬性觀察者。

24

構(gòu)造器并不像函數(shù)和方法那樣在括號(hào)前有一個(gè)可辨別的名字。因此在調(diào)用構(gòu)造器時(shí),主要通過構(gòu)造器中的參數(shù)名和類型來確定應(yīng)該被調(diào)用的構(gòu)造器。正因?yàn)閰?shù)如此重要,如果你在定義構(gòu)造器時(shí)沒有提供參數(shù)的外部名字,Swift 會(huì)為構(gòu)造器的每個(gè)參數(shù)自動(dòng)生成一個(gè)跟內(nèi)部名字相同的外部名。方法名都是init,不需要func關(guān)鍵字

struct Color {
    let red, green, blue: Double
    init(red: Double, green: Double, blue: Double) {
        self.red   = red
        self.green = green
        self.blue  = blue
    }
    init(white: Double) {
        red   = white
        green = white
        blue  = white
    }
}

25

因此我們將屬性聲明可選類型。當(dāng)實(shí)例化時(shí),它將自動(dòng)賦值為nil,表明暫時(shí)還沒有值。

26

你可以在構(gòu)造過程中的任意時(shí)間點(diǎn)給常量屬性指定一個(gè)值,只要在構(gòu)造過程結(jié)束時(shí)是一個(gè)確定的值。一旦常量屬性被賦值,它將永遠(yuǎn)不可更改。

注意
對(duì)于類的實(shí)例來說,它的常量屬性只能在定義它的類的構(gòu)造過程中修改;不能在子類中修改。

27

如果結(jié)構(gòu)體或類的所有屬性都有默認(rèn)值,同時(shí)沒有自定義的構(gòu)造器,那么 Swift 會(huì)給這些結(jié)構(gòu)體或類提供一個(gè)默認(rèn)構(gòu)造器(default initializers)。這個(gè)默認(rèn)構(gòu)造器將簡(jiǎn)單地創(chuàng)建一個(gè)所有屬性值都設(shè)置為默認(rèn)值的實(shí)例。自動(dòng)獲得的默認(rèn)構(gòu)造器總會(huì)是類中的指定構(gòu)造器
屬性有默認(rèn)值,可以用默認(rèn)構(gòu)造器,如果是類,他的實(shí)例屬性沒有默認(rèn)值則不能使用默認(rèn)構(gòu)造器,而結(jié)構(gòu)體在沒有默認(rèn)值得情況下可以使用結(jié)構(gòu)體的逐一成員構(gòu)造器

class ShoppingListItem {
    var name: String?
    var quantity = 1
    var purchased = false
}
var item = ShoppingListItem()//默認(rèn)構(gòu)造器

結(jié)構(gòu)體的逐一成員構(gòu)造器,如果結(jié)構(gòu)體沒有提供自定義的構(gòu)造器,它們將自動(dòng)獲得一個(gè)逐一成員構(gòu)造器,即使結(jié)構(gòu)體的存儲(chǔ)型屬性沒有默認(rèn)值。

struct Size {
    var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)

28

指定構(gòu)造器和便利構(gòu)造器的語(yǔ)法

類的指定構(gòu)造器的寫法跟值類型簡(jiǎn)單構(gòu)造器一樣:

init(parameters) {
    statements
}

便利構(gòu)造器也采用相同樣式的寫法,但需要在init關(guān)鍵字之前放置convenience關(guān)鍵字,并使用空格將它們倆分開:

convenience init(parameters) {
    statements
}
指定構(gòu)造器必須總是向上代理(向上找構(gòu)造器方法來構(gòu)造,向上招代理)
便利構(gòu)造器必須總是橫向代理(便利構(gòu)造器必須最終導(dǎo)致一個(gè)指定構(gòu)造器被調(diào)用)

29

跟 Objective-C 中的子類不同,Swift 中的子類默認(rèn)情況下不會(huì)繼承父類的構(gòu)造器。Swift 的這種機(jī)制可以防止一個(gè)父類的簡(jiǎn)單構(gòu)造器被一個(gè)更精細(xì)的子類繼承,并被錯(cuò)誤地用來創(chuàng)建子類的實(shí)例。父類的構(gòu)造器僅會(huì)在安全和適當(dāng)?shù)那闆r下被繼承

繼承
假設(shè)你為子類中引入的所有新屬性都提供了默認(rèn)值,以下 2 個(gè)規(guī)則適用:
規(guī)則 1
如果子類沒有定義任何指定構(gòu)造器,它將自動(dòng)繼承所有父類的指定構(gòu)造器。
規(guī)則 2
如果子類提供了所有父類指定構(gòu)造器的實(shí)現(xiàn)——無論是通過規(guī)則 1 繼承過來的,還是提供了自定義實(shí)現(xiàn)——它將自動(dòng)繼承所有父類的便利構(gòu)造器。

當(dāng)你在編寫一個(gè)和父類中指定構(gòu)造器相匹配的子類構(gòu)造器時(shí),你實(shí)際上是在重寫父類的這個(gè)指定構(gòu)造器。因此,你必須在定義子類構(gòu)造器時(shí)帶上override修飾符。即使你重寫的是系統(tǒng)自動(dòng)提供的默認(rèn)構(gòu)造器,也需要帶上override修飾符,

相反,如果你編寫了一個(gè)和父類便利構(gòu)造器相匹配的子類構(gòu)造器,由于子類不能直接調(diào)用父類的便利構(gòu)造器(每個(gè)規(guī)則都在上文類的構(gòu)造器代理規(guī)則有所描述),因此,嚴(yán)格意義上來講,你的子類并未對(duì)一個(gè)父類構(gòu)造器提供重寫。最后的結(jié)果就是,你在子類中“重寫”一個(gè)父類便利構(gòu)造器時(shí),不需要加override前綴。

子類可以在初始化時(shí)修改繼承來的變量屬性,但是不能修改繼承來的常量屬性。

構(gòu)造器過程兩個(gè)階段

階段 1

某個(gè)指定構(gòu)造器或便利構(gòu)造器被調(diào)用。
完成新實(shí)例內(nèi)存的分配,但此時(shí)內(nèi)存還沒有被初始化。
指定構(gòu)造器確保其所在類引入的所有存儲(chǔ)型屬性都已賦初值。存儲(chǔ)型屬性所屬的內(nèi)存完成初始化。
指定構(gòu)造器將調(diào)用父類的構(gòu)造器,完成父類屬性的初始化。
這個(gè)調(diào)用父類構(gòu)造器的過程沿著構(gòu)造器鏈一直往上執(zhí)行,直到到達(dá)構(gòu)造器鏈的最頂部。
當(dāng)?shù)竭_(dá)了構(gòu)造器鏈最頂部,且已確保所有實(shí)例包含的存儲(chǔ)型屬性都已經(jīng)賦值,這個(gè)實(shí)例的內(nèi)存被認(rèn)為已經(jīng)完全初始化。此時(shí)階段 1 完成。
階段 2

從頂部構(gòu)造器鏈一直往下,每個(gè)構(gòu)造器鏈中類的指定構(gòu)造器都有機(jī)會(huì)進(jìn)一步定制實(shí)例。構(gòu)造器此時(shí)可以訪問self、修改它的屬性并調(diào)用實(shí)例方法等等。(不是必須)
最終,任意構(gòu)造器鏈中的便利構(gòu)造器可以有機(jī)會(huì)定制實(shí)例和使用self。(不是必須)

例子

class Food {
    var name: String
    init(name: String) {
        self.name = name
    }
    convenience init() {
        self.init(name: "[Unnamed]")
    }
}
class RecipeIngredient: Food {
    var quantity: Int
    init(name: String, quantity: Int) {
        self.quantity = quantity
        super.init(name: name)
    }
    override convenience init(name: String) {
        self.init(name: name, quantity: 1)
    }
}
class ShoppingListItem: RecipeIngredient {
    var purchased = false
    var description: String {
        var output = "\(quantity) x \(name)"
        output += purchased ? " ?" : " ?"
        return output
    }
}

RecipeIngredient的便利構(gòu)造器init(name: String)使用了跟Food中指定構(gòu)造器init(name: String)相同的參數(shù)。由于這個(gè)便利構(gòu)造器重寫了父類的指定構(gòu)造器init(name: String),因此必須在前面使用override修飾符

盡管RecipeIngredient將父類的指定構(gòu)造器重寫為了便利構(gòu)造器,它依然提供了父類的所有指定構(gòu)造器的實(shí)現(xiàn)。因此,RecipeIngredient會(huì)自動(dòng)繼承父類的所有便利構(gòu)造器。

由于它為自己引入的所有屬性都提供了默認(rèn)值,并且自己沒有定義任何構(gòu)造器,ShoppingListItem將自動(dòng)繼承所有父類中的指定構(gòu)造器和便利構(gòu)造器。

30

帶原始值的枚舉類型會(huì)自帶一個(gè)可失敗構(gòu)造器init?(rawValue:),該可失敗構(gòu)造器有一個(gè)名為rawValue的參數(shù),其類型和枚舉類型的原始值類型一致,如果該參數(shù)的值能夠和某個(gè)枚舉成員的原始值匹配,則該構(gòu)造器會(huì)構(gòu)造相應(yīng)的枚舉成員,否則構(gòu)造失敗。

enum TemperatureUnit: Character {
    case Kelvin = "K", Celsius = "C", Fahrenheit = "F"
}

let fahrenheitUnit = TemperatureUnit(rawValue: "F")
if fahrenheitUnit != nil {
    print("This is a defined temperature unit, so initialization succeeded.")
}

31

弱引用 (兩個(gè)屬性的值都允許為nil,并會(huì)潛在的產(chǎn)生循環(huán)強(qiáng)引用)

弱引用不會(huì)保持所引用的實(shí)例,即使引用存在,實(shí)例也有可能被銷毀。因此,

ARC 會(huì)在引用的實(shí)例被銷毀后自動(dòng)將其賦值為nil。并且因?yàn)槿跻每梢栽试S它們的值在運(yùn)行時(shí)被賦值為nil,所以它們會(huì)被定義為可選類型變量,而不是常量。

無主引用(一個(gè)屬性的值允許為nil,而另一個(gè)屬性的值不允許為nil,這也可能會(huì)產(chǎn)生循環(huán)強(qiáng)引用)

和弱引用類似,無主引用不會(huì)牢牢保持住引用的實(shí)例。和弱引用不同的是,無主引用在其他實(shí)例有相同或者更長(zhǎng)的生命周期時(shí)使用。你可以在聲明屬性或者變量時(shí),在前面加上關(guān)鍵字unowned表示這是一個(gè)無主引用。

無主引用通常都被期望擁有值。不過 ARC 無法在實(shí)例被銷毀后將無主引用設(shè)為nil,因?yàn)榉强蛇x類型的變量不允許被賦值為nil。
使用無主引用,你必須確保引用始終指向一個(gè)未銷毀的實(shí)例。
如果你試圖在實(shí)例被銷毀后,訪問該實(shí)例的無主引用,會(huì)觸發(fā)運(yùn)行時(shí)錯(cuò)誤。

無主引用以及隱式解析可選屬性(兩個(gè)屬性都必須有值,并且初始化完成后永遠(yuǎn)不會(huì)為nil。在這種場(chǎng)景中,需要一個(gè)類使用無主屬性,而另外一個(gè)類使用隱式解析可選屬性)

閉包造成的循環(huán)引用

定義捕獲列表來解決
捕獲列表中的每一項(xiàng)都由一對(duì)元素組成,一個(gè)元素是weak或unowned關(guān)鍵字,另一個(gè)元素是類實(shí)例的引用(例如self)或初始化過的變量(如delegate = self.delegate!)

lazy var someClosure: (Int, String) -> String = {
    [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
    // 這里是閉包的函數(shù)體
}
lazy var someClosure: Void -> String = {
    [unowned self, weak delegate = self.delegate!] in
    // 這里是閉包的函數(shù)體
}

32

可選鏈?zhǔn)秸{(diào)用是一種可以在當(dāng)前值可能為nil的可選值上請(qǐng)求和調(diào)用屬性、方法及下標(biāo)的方法。如果可選值有值,那么調(diào)用就會(huì)成功;如果可選值是nil,那么調(diào)用將返回nil。多個(gè)調(diào)用可以連接在一起形成一個(gè)調(diào)用鏈,如果其中任何一個(gè)節(jié)點(diǎn)為nil,整個(gè)調(diào)用鏈都會(huì)失敗,即返回nil。
通過在想調(diào)用的屬性、方法、或下標(biāo)的可選值后面放一個(gè)問號(hào)(?),可以定義一個(gè)可選鏈

john.residence?.address = someAddress
john.residence?.address = createAddress()
可選鏈調(diào)用失敗時(shí)右側(cè)并不會(huì)執(zhí)行
這一點(diǎn)很像在可選值后面放一個(gè)嘆號(hào)(!)來強(qiáng)制展開它的值。它們的主要區(qū)別在于當(dāng)可選值為空時(shí)可選鏈?zhǔn)秸{(diào)用只會(huì)調(diào)用失敗,然而強(qiáng)制展開將會(huì)觸發(fā)運(yùn)行時(shí)錯(cuò)誤。


為了反映可選鏈?zhǔn)秸{(diào)用可以在空值(nil)上調(diào)用的事實(shí),不論這個(gè)調(diào)用的屬性、方法及下標(biāo)返回的值是不是可選值,它的返回結(jié)果都是一個(gè)可選值
特別地,可選鏈?zhǔn)秸{(diào)用的返回結(jié)果與原本的返回結(jié)果具有相同的類型,但是被包裝成了一個(gè)可選值。例如,使用可選鏈?zhǔn)秸{(diào)用訪問屬性,當(dāng)可選鏈?zhǔn)秸{(diào)用成功時(shí),如果屬性原本的返回結(jié)果是Int類型,則會(huì)變?yōu)镮nt?類型

通過可選鏈?zhǔn)秸{(diào)用調(diào)用方法

這個(gè)方法沒有返回值。然而,沒有返回值的方法具有隱式的返回類型Void,這意味著沒有返回值的方法也會(huì)返回()
如果在可選值上通過可選鏈?zhǔn)秸{(diào)用來調(diào)用這個(gè)方法,該方法的返回類型會(huì)是Void?,而不是Void,因?yàn)橥ㄟ^可選鏈?zhǔn)秸{(diào)用得到的返回值都是可選的。這樣我們就可以使用if語(yǔ)句來判斷能否成功調(diào)用printNumberOfRooms()方法,即使方法本身沒有定義返回值。通過判斷返回值是否為nil可以判斷調(diào)用是否成功:

if john.residence?.printNumberOfRooms() != nil {
    print("It was possible to print the number of rooms.")
} else {
    print("It was not possible to print the number of rooms.")
}
// 打印 “It was not possible to print the number of rooms.”

同樣的,可以據(jù)此判斷通過可選鏈?zhǔn)秸{(diào)用為屬性賦值是否成功。在上面的通過可選鏈?zhǔn)秸{(diào)用訪問屬性的例子中,我們嘗試給john.residence中的address屬性賦值,即使residencenil。通過可選鏈?zhǔn)秸{(diào)用給屬性賦值會(huì)返回Void?,通過判斷返回值是否為nil就可以知道賦值是否成功:

if (john.residence?.address = someAddress) != nil {
    print("It was possible to set the address.")
} else {
    print("It was not possible to set the address.")
}
// 打印 “It was not possible to set the address.”

通過可選鏈?zhǔn)秸{(diào)用,我們可以在一個(gè)可選值上訪問下標(biāo),并且判斷下標(biāo)調(diào)用是否成功

if let firstRoomName = john.residence?[0].name {
    print("The first room name is \(firstRoomName).")
} else {
    print("Unable to retrieve the first room name.")
}
// 打印 “Unable to retrieve the first room name.”

如果你訪問的值不是可選的,可選鏈?zhǔn)秸{(diào)用將會(huì)返回可選值。

如果你訪問的值就是可選的,可選鏈?zhǔn)秸{(diào)用不會(huì)讓可選返回值變得“更可選”。

33 錯(cuò)誤補(bǔ)捉

一個(gè) throwing 函數(shù)可以在其內(nèi)部拋出錯(cuò)誤,并將錯(cuò)誤傳遞到函數(shù)被調(diào)用時(shí)的作用域。

注意
只有 throwing 函數(shù)可以傳遞錯(cuò)誤。任何在某個(gè)非 throwing 函數(shù)內(nèi)部拋出的錯(cuò)誤只能在函數(shù)內(nèi)部處理。

34

用類型檢查操作符(is)來檢查一個(gè)實(shí)例是否屬于特定子類型。若實(shí)例屬于那個(gè)子類型,類型檢查操作符返回 true,否則返回 false。
某類型的一個(gè)常量或變量可能在幕后實(shí)際上屬于一個(gè)子類。當(dāng)確定是這種情況時(shí),你可以嘗試向下轉(zhuǎn)到它的子類型,用類型轉(zhuǎn)換操作符(as? 或 as!)。
Any 可以表示任何類型,包括函數(shù)類型。 AnyObject 可以表示任何類類型的實(shí)例。

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

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

  • Swift屬性 Swift屬性將值跟特定的類,結(jié)構(gòu)體,枚舉關(guān)聯(lián)。分為存儲(chǔ)屬性和計(jì)算屬性,通常用于特定類型的實(shí)例。屬...
    小小廚師閱讀 969評(píng)論 0 0
  • 一直沒有時(shí)間好好看一下swift,最近復(fù)習(xí)了一遍語(yǔ)法,這里記錄swift學(xué)習(xí)過程中遇到的一些問題和要點(diǎn),和Obje...
    bomo閱讀 2,544評(píng)論 0 25
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young閱讀 4,169評(píng)論 1 10
  • swift的基礎(chǔ)語(yǔ)法 這樣吧,先把swift4.0教材的先分享給大家。swift4和swift3的基本上沒有多大的...
    請(qǐng)輸入賬號(hào)名閱讀 15,916評(píng)論 12 60
  • 清晨,雨落,朝露,晦陽(yáng) 半夢(mèng)半醒,夢(mèng)中由一幅發(fā)黃的樹葉引發(fā),雖已記不起具體橋段,可是一睜眼,眼前浮現(xiàn)這鏡子中映出的...
    現(xiàn)哉ocean閱讀 186評(píng)論 0 0

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