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

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

從語境中推斷類型:

因排序閉包為實(shí)際參數(shù)來傳遞給函數(shù),故 Swift 能推斷它的形式參數(shù)類型和返回類型。 sorted(by:) 方法期望它的第二個(gè)形式參數(shù)是一個(gè) (String, String) -> Bool 類型的函數(shù)。這意味著 (String, String)和 Bool 類型不需要被寫成閉包表達(dá)式定義中的一部分,因?yàn)樗械念愋投寄鼙煌茢啵祷丶^ ( ->) 和圍繞在形式參數(shù)名周圍的括號(hào)也能被省略:

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

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

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

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

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

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

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

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

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

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

reversedNames = names.sorted(by: >)

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

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

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

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

3,逃逸閉包:

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

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

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

? ? completionHandlers.append(completionHandler)

}

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

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

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

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

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

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

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

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

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

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

}

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

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

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

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

? ? customerProviders.append(customerProvider)

}

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

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屬性來訪問一個(gè)枚舉成員的原始值:let sunsetDirection = CompassPoint.west.rawValue;

遞歸枚舉:

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

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

enum ArithmeticExpression {

? ? case number(Int)

? ? indirect case addition(ArithmeticExpression, ArithmeticExpression)

? ? indirect case multiplication(ArithmeticExpression, ArithmeticExpression)

}

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

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

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

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

struct Resolution {

? ? ? var width = 0

? ? ? var height = 0

? }

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

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

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

相同于 ( ===)

不相同于( !==)

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

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

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

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

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

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

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

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

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

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

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

必須把延遲存儲(chǔ)屬性聲明為變量(使用 var 關(guān)鍵字),因?yàn)樗某跏贾悼赡茉趯?shí)例初始化完成之前無法取得。常量屬性則必須在初始化完成之前有值,因此不能聲明為延遲。如果被標(biāo)記為 lazy 修飾符的屬性同時(shí)被多個(gè)線程訪問并且屬性還沒有被初始化,則無法保證屬性只初始化一次。

14,簡(jiǎn)寫設(shè)置器(setter)聲明:如果一個(gè)計(jì)算屬性的設(shè)置器沒有為將要被設(shè)置的值定義一個(gè)名字,那么他將被默認(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,只讀計(jì)算屬性:一個(gè)有讀取器但是沒有設(shè)置器的計(jì)算屬性就是所謂的只讀計(jì)算屬性。只讀計(jì)算屬性返回一個(gè)值,也可以通過點(diǎn)語法訪問,但是不能被修改為另一個(gè)值。

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

struct Cuboid {

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

? ? var volume: Double {

? ? ? ? return width * height * depth

? ? }

}

16,必須用 var 關(guān)鍵字定義計(jì)算屬性——包括只讀計(jì)算屬性——為變量屬性,因?yàn)樗鼈兊闹挡皇枪潭ǖ摹?/p>

屬性觀察者:父類屬性的 willSet 和 didSet 觀察者會(huì)在子類初始化器中設(shè)置時(shí)被調(diào)用。它們不會(huì)在類的父類初始化器調(diào)用中設(shè)置其自身屬性時(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,類型屬性語法:

對(duì)于類類型的計(jì)算類型屬性,你可以使用 class 關(guān)鍵字來允許子類重寫父類的實(shí)現(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)情況下,值類型屬性不能被自身的實(shí)例方法修改。

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

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

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

在異變方法里指定自身:

異變方法可以指定整個(gè)實(shí)例給隱含的 self屬性。上文中那個(gè) 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)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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