如何僞裝寫過 Swift

總結了筆者平常使用 Swift 的一些小 Tips。html

Safe & Fast

1. 能用 let,儘可能不用 var

把代碼裏的 var 全改爲 let,只保留不能編譯經過的。git

ObjC 的 Foundation 層幾乎都是繼承 NSObject 實現的,平時都在操做指針,因此要區分 Mutable 和 Imutable 的設計,好比 NSStringNSMutableStringgithub

Swift 使用了 let 和 var 關鍵字直接用於區分是否可變。可變會更容易出錯,因此儘可能採用不可變設計,等到須要改變才改成 var 吧。objective-c

2. 儘可能不用 !

!遇到 nil 時會 crash(包括 as! 進行強制轉換)。可使用 if let/guard let/case let 配合 as? 將可選值消化掉。可能返回 nil 的 API,爲何要本身騙本身呢?編程

當遇到 ObjC 代碼暴露給 Swift 使用時,給接口 .h 文件加上 NS_ASSUME_NONNULL_BEGINNS_ASSUME_NONNULL_END 並檢查接口參數是否能夠爲 nil 吧。swift

3. 多定義 struct,少定義 class

struct 是值類型,class 是引用類型。類類型分配在堆區,默認淺拷貝,容易被不經意間被改變,而值類型分配在棧區,默認深拷貝。而且 Swift 還有寫時複製(copy on write)。markdown

即便是使用 class 時,也僅在必要時(如橋接到 ObjC,使用 Runtime 一些特性)繼承自 NSObject網絡

4. 能用 Swift 標準庫類型,儘可能不用對應的 Foundation 類型

多使用 StringArrayDictionaryIntBool,少使用 Foundation 裏面的 NSStringNSArrayNSDictionaryNSNumber。Cocoa Foundation 裏面的都是類類型,而 Swift 標準庫的是值類型,有不少標準庫的方便方法。閉包

還有用 print 代替 NSLogapp

5. 優先使用內置高階函數

forEachmapcompactMapflatMapzipreduce 是好幫手,代替一些使用變量並在循環中處理的例子吧。用上高階函數,不只代碼更清晰,還能將狀態控制在更小的做用域內。

6. 使用 try catch 捕獲錯誤

和 ObjC 基本都在函數的回調中返回 NSError 不同,Swift 函數可使用 throw 關鍵字拋出錯誤。

func test() throws {
	 //... 
}

do {
  try test()
} catch {
  print(error)
}

// 若是對錯誤不敏感
try? test()
複製代碼

7. 對 String 判空時優先採用 isEmpty

Swift 裏面的 String 的 index 和 count 不是一一對應的(兼容 Unicode),因此 stirng.count == 0 的效率不如 string.isEmpty

Clean Code

8. 文件名字去掉前綴

Swift 有着 framework 級別的命名空間,因此命名重複時能夠經過 framework 名肯定,不用擔憂重複命名問題。

儘可能只有在須要橋接給 ObjC 時,才使用 @objc(前綴 + 類名) 進行別名聲明。

9. 省略 self

對應訪問成員變量,方法時,都不用像 ObjC 那些寫 self 了。

只在閉包內、函數實參和成員變量名字相同和方法形參須要自身時使用。閉包內 self 是強制的,而且能夠提醒注意循環引用問題。函數調用時實參名和成員變量相同時,函數做用域內會優先使用函數實參,因此訪問成員變量是須要 self

10. 省略 init()

直接使用 ClassA(),代替 ClassA.init(),代碼更簡潔。

11. 能推導的類型不用顯式編寫

// no bad
let flag:Bool = false
// better
let flag = false

// not bad
view.contentMode = UIView.ContentMode.center
// better
view.contentMode = .center
複製代碼

12. 使用默認形參,簡化接口設計

在設計接口時,再也不須要爲每個形參是否須要而編寫一個方法了,減小方法數吧。

// not bad
func test() {
    //...
}

func test(param1:String) {
    //...
}

func test(param2:String) {
    //...
}

// better
func test(param1:String = "", param2:String = "") {
    //...
}
複製代碼

13. 使用 _ 表示不使用的返回值

var a = [0]
let _ = a.removeLast()

[0].forEach{ _ in print("hh")}
複製代碼

而對於本身設計的接口,若是返回值可有可無,只是附加功能的話,可使用 @discardableResult 進行標註。

14. 使用 `` 來定義和關鍵字重名的方法和屬性

好比系統有個 default 關鍵字,而你也但願使用這個命名時就能派上用場。

let `default` = A()

func `default`() {}
複製代碼

15. Strong-Weak Dance 很簡單

比起 ObjC 裏須要每次須要寫

__weak typeof(self) weak_self = self;
__typeof__(self) strong_self = weak_self;
複製代碼

儘管不少人會採用宏來簡化,但重複的宏定義又會衝突,且 ObjC 沒有訪問權限關鍵字。

Swift 在閉包中可使用 weakunowned 指定閉包對值的捕獲,配合 guard 就能夠實現一樣的功能。

test(){ [weak self] in 
		guard let self = self else { return }
    // self is strong without retain cycle
}
複製代碼

16. 類型嵌套

類型嵌套用於在類型裏定義類型,讓類型的命名空間的精細化程度更高。

struct GameService {
    enum APIError {
        enum ResultError {
            case noResult
        }
    }
  	// use APIError
}
// use GameService.APIError
複製代碼

17. func 嵌套

有時候某一塊邏輯只須要在方法內複用或者作邏輯分割,能夠在方法內定義方法,這樣訪問域會更清晰。

func big(){
	func small() {
    //...
  }
  small()
}
複製代碼

18. 使用閉包作初始化

有時候初始化時一個對象時還須要賦值其中的一些屬性,這個時候就可使用閉包代碼塊的整合。

let someView: UIView = {
	let view = UIView(frame:.zero)
  view.backgroundColor = .red
  return view
}()
複製代碼

19. 使用更簡潔的函數實參和形參

和 ObjC 不一樣,有形參實參的 Swift,能夠在調用和編寫的時候都有更合適簡潔的表達。

// not bad
func updateWithView(view:UIView)
updateWithView(view:viewA)
// better
func updateWithView(_ view:UIView)
updateWith(viewA)

// not bad
func didSelectAtIndex(index:Int)
didSelectAtIndex(index:2)
// better
func didSelect(at index:Int)
didSelect(at:2)
複製代碼

20. Enum 用於命名空間聲明

定義一些常量時,用命名空間作隔離是最好的,Swift 的 Enum 比較適合用於命名空間的定義,能嵌套,且不存在初始化方法不會被用於其餘做用。

enum Event {
    enum Name {
        static let login = "event.name.login"
    }
}

// use 
Event.Name.login
// not allow
Event()
複製代碼

21. 使用 ?? 返回默認值

// not bad
var name:String?
if let aName = dic["name"] as? String {
	name = aName
} else {
  name = ""
}

// better
let name = dic["name"] as? String ?? ""
複製代碼

22. 使用字符串插值

除了常規的字符串插值,Swift5 還增長了更強大可自定義的字符串插值系統,詳情見 文章

let a = 2
print("\(a) is 2")
複製代碼

Syntactic sugar

23. 更 POP(Protocol Oriented Programming,面向協議編程)

Swift 在設計上,爲協議作了不少強大的功能。Swift 標準庫裏大量的方法和類都使用了協議進行抽象。在編寫代碼時優先考慮使用協議進行邏輯的抽象,詳情能夠參考 Apple WWDC 2015 Session 408 - Protocol-Oriented Programming in Swift

24. 優先使用 guard

guardif 的反義詞,能夠提早將異常狀況 return。配合 guard let 使用,能夠在正常分支下使用正確的條件。

guard let a = a as? String else { return }
// 下面的 a 就是 string 而且 non-nil 的了
複製代碼

25. 嘗試元組

元組(Tuple)是個包含多個值的簡單對象,使用元組,能夠簡單的用來函數返回多參數,也能夠在集合類型中存取一對對的值。

typealias Pair<T> = (T, T)

let pair = Pair(1, 2)
複製代碼

26. 嘗試範型

比起 ObjC 僅支持在集合類型裏使用輕量級範型,Swift 的範型更強大,除了集合,還支持類、枚舉、協議(Associate Type)。

protocol View {
    associatedtype Model
    
    func update(model:Model)
}
複製代碼

27. 嘗試枚舉

比起 ObjC 那和 C 語言差很少的枚舉,Swift 的枚舉更強大。Swift 的枚舉不必定須要 Int 做爲枚舉的原始值,能夠不須要原始值,也可使用 StringFloatBoolean 做爲原始值。能在枚舉值上關聯值,實現不少有趣的功能(Rx,Promise 的狀態機)。能給枚舉編寫函數,能給枚舉增長 Extension。

enum State<Value> {
    case pending
    case fulfill(value:Value)
    case reject(reason:Error)
    mutating func update(to state:State){
        guard case .pending = self else {
            return
        }
        self = state
    }
}
複製代碼

28. 嘗試 Extension

和 ObjC 的 Categories 相似,拓展能夠添加類的方法。Swift 的 Extension 還能拓展值類型,枚舉的方法,且不須要新建文件編寫和支持權限訪問關鍵字。經過 Extension,還能給 Protocol 增長默認實現。也能在 Extension 中遵循協議,讓方法劃分更加清晰。

fileprivate extension Date {
    var toString: String {
        //...
    }
}

Date().toString
複製代碼

29. lazy 關鍵字

懶加載不須要像 ObjC 同樣重寫 getter 方法,並判空了,在屬性前面加上 lazy 關鍵字就能夠實現了。

lazy var view = UIView(frame:.zero)
複製代碼

30. where 關鍵字

where 關鍵字能夠對範圍進行限定,詳情見這篇 文章

31. typealias 關鍵字

typealias 能夠用來命名閉包類型、協議類型、範型類型,還支持組合。更多用法見 文章

typealias NewName<D> = ClassA<D>&ProtocolA&ProtocolB
複製代碼

32. Result 類型

Result 是一個枚舉類型,包含成功或者失敗的枚舉值,並持有相應成功或者失敗的值,經過範型肯定類型信息。由於成功和失敗是互斥的,這樣就能夠避免多個可選參數返回。

好比對網絡請求回調進行改造:

URLSession.shared.dataTask(with: request) { result in
    switch result {
    case .success(let (data, _)):
        handle(data: data)
    case .failure(let error):
        handle(error: error)
    }
}
複製代碼

33. KVO

在 ObjC 中,開發者更習慣用相似 FBKVOController 等第三方庫進行 KVO 的監聽,那是由於原生的寫法太難用了。Swift 爲 KVO 增長了閉包的 API,更簡潔好用。

scrollObserver = observe(\.scrollView!.contentOffset, options: [.new], changeHandler: { object, change in
     //...
})
複製代碼

同理,Swift 的 GCD API 也是專門通過 Swift 化的,也更加簡潔好用。

34. Codable

在 Swift4 加入 Codable 協議後,JSON 等通用結構轉模型,終於有了原生的支持。詳情見 文章

35. SwiftUI&Combine

在 WWDC19 推出的 Swift Only 的庫,SwiftUI 有着相似 React 的聲明式 UI 開發框架,配合實時調試,在 Demo 和簡單頁面,跨 Apple 平臺應用適配時有必定優點。而 Combine 時相似 RxSwift 的響應式編程框架,能使事件流更統一。

關於 SwiftUI 和 Combine 的介紹,能夠參考 WWDC 2019 相關 Session。也能夠參考筆者翻譯的 文章 1文章 2

最後

若有錯誤,歡迎交流&指出。

參考連接

相關文章
相關標籤/搜索