swift 4.0> 進(jìn)階知識點全面梳理(二)

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

從語境中推斷類型:

因排序閉包為實際參數(shù)來傳遞給函數(shù),故 Swift 能推斷它的形式參數(shù)類型和返回類型。 sorted(by:) 方法期望它的第二個形式參數(shù)是一個 (String, String) -> Bool 類型的函數(shù)。這意味著 (String, String)和 Bool 類型不需要被寫成閉包表達(dá)式定義中的一部分,因為所有的類型都能被推斷,返回箭頭 ( ->) 和圍繞在形式參數(shù)名周圍的括號也能被省略:

reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )

從單表達(dá)式閉包隱式返回:

單表達(dá)式閉包能夠通過從它們的聲明中刪掉 return 關(guān)鍵字來隱式返回它們單個表達(dá)式的結(jié)果,前面的栗子可以寫作:

reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )

sorted(by:) 方法的第二個實際參數(shù)的函數(shù)類型已經(jīng)明確必須通過閉包返回一個 Bool 值。因為閉包的結(jié)構(gòu)包涵返回 Bool 值的單一表達(dá)式 (s1 > s2),因此沒有歧義,并且 return 關(guān)鍵字能夠被省略。

簡寫的實際參數(shù)名:Swift 自動對行內(nèi)閉包提供簡寫實際參數(shù)名,你也可以通過 $0 , $1 , $2 等名字來引用閉包的實際參數(shù)值;

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

$0 和 $1 分別是閉包的第一個和第二個 String 實際參數(shù)。

運(yùn)算符函數(shù):

Swift 的 String 類型定義了關(guān)于大于號( >)的特定字符串實現(xiàn),讓其作為一個有兩個 String 類型形式參數(shù)的函數(shù)并返回一個 Bool 類型的值。這正好與? sorted(by:) 方法的第二個形式參數(shù)需要的函數(shù)相匹配。因此,你能簡單地傳遞一個大于號,并且 Swift 將推斷你想使用大于號特殊字符串函數(shù)實現(xiàn):

reversedNames = names.sorted(by: >)

尾隨閉包:reversedNames = names.sorted() { $0 > $1 }

如果閉包表達(dá)式作為函數(shù)的唯一實際參數(shù)傳入,而你又使用了尾隨閉包的語法,那你就不需要在函數(shù)名后邊寫圓括號了:

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

2,捕獲值:在 Swift 中,一個能夠捕獲值的閉包最簡單的模型是[表情]內(nèi)嵌函數(shù),即被書寫在另一個函數(shù)的內(nèi)部。一個內(nèi)嵌函數(shù)能夠捕獲外部函數(shù)的實際參數(shù)并且能夠捕獲任何在外部函數(shù)的內(nèi)部定義了的常量與變量。

3,逃逸閉包:

當(dāng)閉包作為一個實際參數(shù)傳遞給一個函數(shù)的時候,我們就說這個閉包逃逸了,因為它可以在函數(shù)返回之后被調(diào)用。當(dāng)你聲明一個接受閉包作為形式參數(shù)的函數(shù)時,你可以在形式參數(shù)前寫 @escaping 來明確閉包是允許逃逸的;閉包可以逃逸的一種方法是被儲存在定義于函數(shù)外的變量里;

舉例:var completionHandlers: [() -> Void] = []

func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {

? ? completionHandlers.append(completionHandler)

}

如果你不標(biāo)記函數(shù)的形式參數(shù)為 @escaping ,你就會遇到編譯時錯誤。

讓閉包 @escaping 意味著你必須在閉包中顯式地引用 self ,比如說,下面的代碼中,傳給 someFunctionWithEscapingClosure(_:) 的閉包是一個逃逸閉包,也就是說它需要顯式地引用 self 。相反,傳給 someFunctionWithNonescapingClosure(_:) 的閉包是非逃逸閉包,也就是說它可以隱式地引用 self 。

個人見解: 逃逸閉包需要顯示引用self,是因為逃逸閉包是被存儲于函數(shù)外的變量里,用self來聲明此閉包的歸屬對象,以便在其他類中,此閉包調(diào)用時,能夠很好的區(qū)分來源

4,自動閉包是一種自動創(chuàng)建的用來把作為實際參數(shù)傳遞給函數(shù)的表達(dá)式打包的閉包;它不接受任何實際參數(shù),并且當(dāng)它被調(diào)用時,它會返回內(nèi)部打包的表達(dá)式的值。這個語法的好處在于通過寫普通表達(dá)式代替顯式閉包而使你省略包圍函數(shù)形式參數(shù)的括號。

var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

let customerProvider = { customersInLine.remove(at: 0) }//自動閉包

盡管 customersInLine 數(shù)組的第一個元素以閉包的一部分被移除了,但任務(wù)并沒有執(zhí)行直到閉包被實際調(diào)用。如果閉包永遠(yuǎn)不被調(diào)用,那么閉包里邊的表達(dá)式就永遠(yuǎn)不會求值。[表情]注意 customerProvider 的類型不是 String 而是? () -> String ——一個不接受實際參數(shù)并且返回一個字符串的函數(shù);

當(dāng)你傳一個閉包作為實際參數(shù)到函數(shù)的時候,你會得到與延遲處理相同的行為。

func serve(customer customerProvider: () -> String) {

? ? print("Now serving \(customerProvider())!")

}

serve(customer: { customersInLine.remove(at: 0) } )

上邊的函數(shù) serve(customer:) 接收一個明確的返回下一個客戶名稱的閉包。下邊的另一個版本的 serve(customer:) 執(zhí)行相同的任務(wù)但是不使用明確的閉包而是通過 @autoclosure 標(biāo)志標(biāo)記它的形式參數(shù)使用了自動閉包?,F(xiàn)在你可以調(diào)用函數(shù)就像它接收了一個 String 實際參數(shù)而不是閉包。實際參數(shù)自動地轉(zhuǎn)換為閉包,[表情]因為 customerProvider 形式參數(shù)的類型被標(biāo)記為 @autoclosure 標(biāo)記。實際如此:serve(customer customerProvider: @autoclosure () -> String)

5,如果你想要自動閉包允許逃逸,就同時使用 @autoclosure 和 @escaping 標(biāo)志,

func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {

? ? customerProviders.append(customerProvider)

}

6,在 Swift 中,為不同類型產(chǎn)品條碼定義枚舉大概是這種姿勢:

enum Barcode {

? ? case upc(Int, Int, Int, Int)

? ? case qrCode(String)

}

switch productBarcode {

case .upc(let numberSystem, let manufacturer, let product, let check):

? ? print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")

case .qrCode(let productCode):

? ? print("QR code: \(productCode).")

}

switch productBarcode {

case let .upc(numberSystem, manufacturer, product, check):

? ? print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).")

case let .qrCode(productCode):

? ? print("QR code: \(productCode).")

}

7,枚舉:enum CompassPoint: String {

? ? case north, south, east, west

}可以用 rawValue屬性來訪問一個枚舉成員的原始值:let sunsetDirection = CompassPoint.west.rawValue;

遞歸枚舉:

遞歸枚舉是擁有另一個枚舉作為枚舉成員關(guān)聯(lián)值的枚舉;當(dāng)編譯器操作遞歸枚舉時[表情]必須插入間接尋址層,你可以在聲明枚舉成員之前使用 indirect關(guān)鍵字來明確它是遞歸的;

例如:儲存簡單數(shù)學(xué)運(yùn)算表達(dá)式的枚舉:

enum ArithmeticExpression {

? ? case number(Int)

? ? indirect case addition(ArithmeticExpression, ArithmeticExpression)

? ? indirect case multiplication(ArithmeticExpression, ArithmeticExpression)

}

你同樣可以在枚舉之前寫 indirect 來讓整個枚舉成員在需要時可以遞歸:

indirect enum ArithmeticExpression {

? ? case number(Int)

? ? case addition(ArithmeticExpression, ArithmeticExpression)

? ? case multiplication(ArithmeticExpression, ArithmeticExpression)

}

示例:let five = ArithmeticExpression.number(5)

let four = ArithmeticExpression.number(4)

let sum = ArithmeticExpression.addition(five, four)

let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))

8,struct Resolution {

? ? ? var width = 0

? ? ? var height = 0

? }存儲屬性是綁定并儲存在類或者結(jié)構(gòu)體中的常量或者變量。這兩個屬性因以值 0 來初始化,所以它們的類型被推斷為 Int 。

9,所有的結(jié)構(gòu)體都有一個自動生成的成員初始化器,你可以使用它來初始化新結(jié)構(gòu)體實例的成員屬性。

struct Resolution {

? ? ? var width = 0

? ? ? var height = 0

? }

let vga = Resolution(width: 640, height: 480)

10,結(jié)構(gòu)體和枚舉是值類型;實際上,Swift 中所有的基本類型——整數(shù),浮點數(shù),布爾量,字符串,數(shù)組和字典——都是值類型,并且都以結(jié)構(gòu)體的形式在后臺實現(xiàn)。Swift 中所有的結(jié)構(gòu)體和枚舉都是值類型,這意味著你所創(chuàng)建的任何結(jié)構(gòu)體和枚舉實例——和實例作為屬性所包含的任意值類型——在代碼傳遞中總是被拷貝的。

11,有時候找出兩個常量或者變量是否引用自同一個類實例非常有用,為了允許這樣,Swift提供了兩個特點運(yùn)算符:

相同于 ( ===)

不相同于( !==)

利用這兩個恒等運(yùn)算符來檢查兩個常量或者變量是否引用相同的實例:

結(jié)構(gòu)體實例總是通過值來傳遞,而類實例總是通過引用來傳遞,

按照通用準(zhǔn)則,當(dāng)符合以下一條或多條情形時應(yīng)考慮創(chuàng)建一個結(jié)構(gòu)體:

[表情]結(jié)構(gòu)體的主要目的是為了封裝一些相關(guān)的簡單數(shù)據(jù)值;

當(dāng)你在賦予或者傳遞結(jié)構(gòu)實例時,有理由需要封裝的數(shù)據(jù)值被拷貝而不是引用;

任何存儲在結(jié)構(gòu)體中的屬性是值類型,也將被拷貝而不是被引用;

結(jié)構(gòu)體不需要從一個已存在類型繼承屬性或者行為。

12,存儲屬性會存儲常量或變量作為實例的一部分,反之計算屬性會計算(而不是存儲)值。計算屬性可以由類、結(jié)構(gòu)體和枚舉定義。存儲屬性只能由類和結(jié)構(gòu)體定義。

結(jié)構(gòu)體是值類型。當(dāng)一個值類型的實例被標(biāo)記為常量時,該實例的其他屬性也均為常量。

類來說則不同,它是引用類型。如果你給一個常量賦值引用類型實例,你仍然可以修改那個實例的變量屬性。

13,延遲存儲屬性:通過在其聲明前標(biāo)注 lazy 修飾語來表示一個延遲存儲屬性。

必須把延遲存儲屬性聲明為變量(使用 var 關(guān)鍵字),因為它的初始值可能在實例初始化完成之前無法取得。常量屬性則必須在初始化完成之前有值,因此不能聲明為延遲。如果被標(biāo)記為 lazy 修飾符的屬性同時被多個線程訪問并且屬性還沒有被初始化,則無法保證屬性只初始化一次。

14,簡寫設(shè)置器(setter)聲明:如果一個計算屬性的設(shè)置器沒有為將要被設(shè)置的值定義一個名字,那么他將被默認(rèn)命名為 newValue;以下示例,自定義為newCenter;

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è)置器的計算屬性就是所謂的只讀計算屬性。只讀計算屬性返回一個值,也可以通過點語法訪問,但是不能被修改為另一個值。

通過去掉 get 關(guān)鍵字和他的大擴(kuò)號來簡化只讀計算屬性的聲明:

struct Cuboid {

? ? var width = 0.0, height = 0.0, depth = 0.0

? ? var volume: Double {

? ? ? ? return width * height * depth

? ? }

}

16,必須用 var 關(guān)鍵字定義計算屬性——包括只讀計算屬性——為變量屬性,因為它們的值不是固定的。

屬性觀察者:父類屬性的 willSet 和 didSet 觀察者會在子類初始化器中設(shè)置時被調(diào)用。它們不會在類的父類初始化器調(diào)用中設(shè)置其自身屬性時被調(diào)用;

class StepCounter {

? ? var totalSteps: Int = 0 {

? ? ? ? willSet(newTotalSteps) {

? ? ? ? ? ? print("About to set totalSteps to \(newTotalSteps)")

? ? ? ? }

? ? ? ? didSet {

? ? ? ? ? ? if totalSteps > oldValue? {

? ? ? ? ? ? ? ? print("Added \(totalSteps - oldValue) steps")

? ? ? ? ? ? }

? ? ? ? }

? ? }

}

let stepCounter = StepCounter()

stepCounter.totalSteps = 200

// About to set totalSteps to 200

// Added 200 steps

stepCounter.totalSteps = 360

// About to set totalSteps to 360

// Added 160 steps

17,類型屬性語法:

對于類類型的計算類型屬性,你可以使用 class 關(guān)鍵字來允許子類重寫父類的實現(xiàn);

class SomeClass {

? ? static var storedTypeProperty = "Some value."

? ? static var computedTypeProperty: Int {

? ? ? ? return 27

? ? }

? ? class var overrideableComputedTypeProperty: Int {

? ? ? ? return 107

? ? }

}

18,結(jié)構(gòu)體和枚舉是值類型。默認(rèn)情況下,值類型屬性不能被自身的實例方法修改。

總之,如果你需要在特定的方法中修改結(jié)構(gòu)體或者枚舉的屬性,你可以選擇將這個方法異變。然后這個方法就可以在方法中[表情]異變(嗯,改變)它的屬性了,并且任何改變在方法結(jié)束的時候[表情]都會寫入到原始的結(jié)構(gòu)體中。方法同樣可以指定一個全新的實例給它隱含的 self屬性,并且這個新的實例將會在方法結(jié)束的時候[表情]替換掉現(xiàn)存的這個實例。

你可以選擇在[表情] func關(guān)鍵字前放一個 mutating關(guān)鍵字來使用這個行為,

struct Point {

? ? var x = 0.0, y = 0.0

? ? mutating func moveBy(x deltaX: Double, y deltaY: Double) {

? ? ? ? x += deltaX

? ? ? ? y += deltaY

? ? }

}

var somePoint = Point(x: 1.0, y: 1.0)

somePoint.moveBy(x: 2.0, y: 3.0)

print("The point is now at (\(somePoint.x), \(somePoint.y))")

在異變方法里指定自身:

異變方法可以指定整個實例給隱含的 self屬性。上文中那個 Point的栗子可以用下邊的代碼代替;

struct Point {

? ? var x = 0.0, y = 0.0

? ? mutating func moveBy(x deltaX: Double, y deltaY: Double) {

? ? ? ? self = Point(x: x + deltaX, y: y + deltaY)

? ? }

}

枚舉的異變方法可以設(shè)置隱含的 self屬性為相同枚舉里的不同成員:

enum TriStateSwitch {

? ? case off, low, high

? ? mutating func next() {

? ? ? ? switch self {

? ? ? ? case .off:

? ? ? ? ? ? self = .low

? ? ? ? case .low:

? ? ? ? ? ? self = .high

? ? ? ? case .high:

? ? ? ? ? ? self = .off

? ? ? ? }

? ? }

}

var ovenLight = TriStateSwitch.low

ovenLight.next()

?著作權(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)容

  • 86.復(fù)合 Cases 共享相同代碼塊的多個switch 分支 分支可以合并, 寫在分支后用逗號分開。如果任何模式...
    無灃閱讀 1,568評論 1 5
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young閱讀 4,202評論 1 10
  • SwiftDay011.MySwiftimport UIKitprintln("Hello Swift!")var...
    smile麗語閱讀 4,114評論 0 6
  • 【讀經(jīng)】 詩篇29 【金句】 要將耶和華的名所當(dāng)?shù)玫臉s耀歸給他,以圣潔的(的:或譯為)妝飾敬拜耶和華。(詩篇 29...
    chanor閱讀 222評論 0 0
  • 前言 前段時間把可樂灑在電腦上了,大概有1/5的罐裝可樂,緊急之下把電腦翻轉(zhuǎn)過來,萬幸的是電腦沒出問題。就是過了兩...
    Jinbeen閱讀 211,791評論 40 22

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