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