Swifter 100 tips 讀后總結(jié)
-
將protocol的方法2為mutating,原因如下:
- protocol也適用于struct和enum中,如果不添加mutating,那么在struct和enum中將會提示沒有實(shí)現(xiàn)協(xié)議方法,無法通過編譯.
- 如果去掉protocol中的mutating字段,那么struct和中將會報(bào)錯(cuò)說不能改變成員變量.
- 在class 中實(shí)現(xiàn)protocol 方法的時(shí)候,不需要添加mutating字段,因?yàn)閏lass可以隨意更改自己的成員變量,所以在protocol里邊用mutating修飾方法,對于class的實(shí)現(xiàn)是完全透明的,可以當(dāng)做不存在.
-
多元組 (Tuple)
這是一個(gè)oc中沒有使用過的結(jié)合類型,使用它可以幫助我們簡化很多問題,比如:
- 在oc甚至java中,交換2個(gè)變量的值,我們通常會這么寫:
func swapMel<T>(a: inout T, b: inOut T) { let temp = a a = b b = temp }這樣做很容易理解,但是產(chǎn)生了一個(gè)中間變量,這是我們不想看到的結(jié)果,所以有人會這么寫:
func swapMel<T>(a: inOut T, b: inOut T) { a = a ^ b b = b ^ a a = a ^ b }這樣,我們就沒有新增一個(gè)中間變量,就進(jìn)行了2個(gè)變量的值的交換,但是這樣做的代價(jià)就是不容易理解.當(dāng)我們用swift中的Tuple來實(shí)現(xiàn)就簡單多了,并且很容易理解:
func swapMel<T>(a: inOut T, b: inOut T) { (a, b) = (b, a) }這樣我們就完成了2個(gè)變量的交換過程,很簡單.
- oc中的返回值只能有一個(gè),所以在解決某些問題的時(shí)候會顯得很麻煩,比如:
CGRect有一個(gè)輔助方法CGRectDivide,它是將一個(gè)CGRect在一定位置切分成2個(gè)區(qū)域,具體使用方法如下:
CGRect rect = CGRectMake(0, 0, 100, 100); CGRect small; CGRect large; CGRectDivide(rect, &small, &large, 20, CGRectMinXEdge);它將{0, 0, 100, 100}分割成了small:{0, 0, 20, 100} 和 large: {20, 0, 80, 100} 2個(gè)區(qū)域,
下邊我們可以用swift的Tuple來實(shí)現(xiàn),并對比一下:
extension CGRect { func divided(atDistance: CGFload, from fromEdge: CGRectEdge) -> (slice: CGRect, remainder: CGRect) { //... } }使用的時(shí)候,做法如下:
let rect = CGRect(x: 0, y: 0, width: 100, height: 100) let (small, large) = rect.divided(atDistance: 20, from: .minXEdge)這樣看起來就很簡單明了
-
@autoclosure 和 ??
- @autoclosure做的事情就是吧一句表達(dá)式自動封裝成一個(gè)閉包(closure).
比如我們有一個(gè)方法接受一個(gè)閉包參數(shù),當(dāng)閉包執(zhí)行結(jié)果為true的時(shí)候進(jìn)行打印:
func logIfTrue(_ predicate: () -> Bool) { if predicate() { print("True") } }在調(diào)用的時(shí)候,我們會這樣寫:
logIfTrue({return 2 > 1})swift中對閉包的寫法進(jìn)行了一些簡化,當(dāng)只有一條return語句的時(shí)候,我們可以這樣寫:
logIfTrue({2 > 1})因?yàn)檫@個(gè)閉包是logIfTrue函數(shù)的最后一個(gè)參數(shù),也就是尾隨閉包,所以我們還可以簡寫如下:
logIfTrue{2 > 1}猶豫簡寫的過多,這樣看起來其實(shí)已經(jīng)不那么好理解了,這時(shí)候我們就可以使用@autoclosure:
func logIfTrue(_ predicate: @autoclosure () -> Bool) { if predicate() { print("True") } }這時(shí)候我們就可以直接寫:
logIfTrue(2 > 1)他會自動把 2 > 1這個(gè)表達(dá)式轉(zhuǎn)換成 () -> Bool,這樣我么就得到一個(gè)寫法簡單,表意清除的式子.
- ??
?? 使用來快速判斷nil的.語意是:如果??操作符左邊的值是非nil的Optional值,就返回他的value,如果是nil,就有??操作符右邊的值代替,比如:
var level: Int? var startLevel = 1 var currentLevel = level ?? startLevel注意:@autoclosure并不支持帶有輸入?yún)?shù)的方法,也就是形如() -> T的參數(shù)才能使用這個(gè)特性進(jìn)行簡化.
// ?? 的2中形式和底層實(shí)現(xiàn) func ??<T>(optional: T?, defaultValue: @autoclosure () -> T?) -> T? func ??<T>(optional: T?, defaultValue: @autoclosure () -> T) -> T // 我們之前用到的是第二種 func ??<T>(optional: T?, defaultValue: @autoclosure () -> T) -> T { switch optinal { case .Some(let value): return value case .None: return defaultValue() } }有人可以會對為什么使用@autoclosure有疑問,說不使用閉包,直接賦值不好嗎? 原因是這樣的,這個(gè)默認(rèn)值可能是經(jīng)過特別復(fù)雜的計(jì)算獲得的,但是它只有在??左側(cè)為nil的時(shí)候才用的到,當(dāng)??左側(cè)非nil的時(shí)候我們根本用不到,所以就不用計(jì)算這個(gè)默認(rèn)值,我們通過這個(gè)閉包,就可以把默認(rèn)值的計(jì)算推遲到optional判定為nil之后,這就是巧妙之處.
另外,swift中的&& 和||操作符其實(shí)也用到了@autoclosure
-
Optional Chaining
使用Optional Chaining可以讓我們拜托很多不必要的判斷和取值,但是使用的時(shí)候要小心,還是有坑的
因?yàn)镺ptional Chaining是隨時(shí)都可能提前返回nil的,所以使用Optional Chaining所得到的東西其實(shí)都是Optional的,比如下邊一段代碼:
class Toy { let name: String init(name: String) { self.name = name } } class Pet { var toy: Toy? } class Child { var pet: Pet? }在實(shí)際使用中,當(dāng)我們想要知道小明的寵物的玩具的名字的時(shí)候,可以通過Optional Chaining來拿到:
let toyName = xiaoming.pet?.toy?.name雖然我們訪問的是name,并且Toy中name被定義為一個(gè)確定的String,而不是String?, 但我們拿到的toyName其實(shí)還是String?類型.因?yàn)樵贠ptional Chaining中隨時(shí)可能會遇到nil而提前返回,這個(gè)時(shí)候我們拿到的就是nil了.
然而,實(shí)際使用中,我們通常會通過Optional Binding(可選綁定)來取值這樣的代碼:
if let toyName = xiaoming.pet?.toy?.name { // 這時(shí)候拿到的toyName必然不是nil }單獨(dú)這樣看還是很清楚的,如果和其他的特性結(jié)合在一起,可能就會很麻煩了:
// 給Toy添加一個(gè)extension // 當(dāng) child 的pet 有toy的時(shí)候就玩,沒有就不能玩 extension Toy { func play() { // ... } }拿小明舉例子, 如果小明的pet有toy的話,就玩之:
xiaoming.pet?toy?.play()如果現(xiàn)在除了小明還有小李小張等,我們需要把這一串調(diào)通抽象出來,做一個(gè)閉包方便使用.傳入一個(gè)child對象,于是我們就可能寫成下邊這個(gè)樣子:
這是錯(cuò)誤的代碼:
let playClosure = {(child: Child) -> () in child.pet?.toy?play() }這樣的代碼是沒有意義的!可能有人會問,問題處在那里呢?
問題就在play()的調(diào)用上.定義的時(shí)候沒有寫play()的返回值,就是Void(同()是等價(jià)的),但是經(jīng)過前面的Optional Chaining以后,我們拿到的是一個(gè)Optional的結(jié)果,也就是我們最后得到的應(yīng)該是一個(gè)closure:
let playClosure = {(child: Child) -> ()? in child.pet?.toy?play() }這樣調(diào)用的返回將是一個(gè)()?(等同于Void?),雖然看起來很奇怪,但這是事實(shí).使用的時(shí)候,我們通過Optional Binding來判定方法是否調(diào)用成功:
if let result: () = playClosure(xiaoming) { print("happy") } else { print("no toy") }