原本這篇文章的標題是「如何寫一個不安全的構造器」,但後面查資料的時候又發現了一些很好玩的東西,就一次性寫成一篇出來,跟你們分享一下 Swift 裏的幾個 best pratice:git
寫 Swift 的人應該很熟悉帶關聯值的枚舉(Enumeration with Associated Value),例如原生的 Optional,錯誤處理的 Result 庫等等,但在我嘗試自定義枚舉的構造器時遇到了這樣的問題:github
enum CustomOptional<Wrapped> {
case value(Wrapped)
case none
init(value: Wrapped) {
return .value(value)
// error: 'nil' is the only return value /
// permitted in an initializer
}
}複製代碼
錯誤提示是構造器裏只可以返回 nil,但如此一來咱們就好像沒有辦法把構造器實現出來了。我想起在使用 Result 的時候有用到過它的構造器,查閱以後,發現正確的作法應該是這樣的:swift
enum CustomOptional<Wrapped> {
case value(Wrapped)
case none
init(value: Wrapped) {
self = .value(value)
}
}複製代碼
順帶說一句,全部的值類型都支持這種寫法。安全
出處:Result — Swift type modelling the success/failure of arbitrary operations閉包
以前我就寫過一篇文章來說這個,之因此再提一次,一方面是爲了文章的完整性,另外一方面就是爲了下文的另外一個語法作鋪墊。app
從 OC 帶過來的命名方式,會讓咱們在閉包裏這麼去寫 strongSelf:ide
block = { [weak self] in
guard let strongSelf = self else { return }
... other code ...
}複製代碼
strongSelf
在代碼裏的出現其實會有點突兀,我會更喜歡利用 Swift 一種語法,讓代碼變得統一:函數
block = { [weak self] in
guard let `self` = self else { return }
... other code ...
}複製代碼
這裏聲明瞭一個局部變量 self
,讓咱們能夠直接用來將捕獲的 weak self 解包出來,因爲 self
是系統關鍵字,使用 ` 包住關鍵字,可讓編譯器把它看作是一個正常的變量名稱。spa
而後咱們在閉包裏使用 self
時,就沒必要考慮它是否會產生循環引用的問題,別的地方的代碼也能夠很方便地複製粘貼過來,不用把 self
所有都改成 strongSelf
。code
出處:忘了😒
感謝大神在評論裏提醒我,原來這是一個編譯的 bug,提案 SE-0079 很詳細地講了這件事情,但目前這個 bug 尚未修復,按照上面的方法去寫就能夠了。
若是這個 bug 被修復了的話,就能夠不必加上 `,能夠直接聲明局部變量 self:
block = { [weak self] in
guard let self = self else { return }
... other code ...
}複製代碼
開頭我提到了這篇文章本來的標題是叫作「如何寫一個不安全的構造器」,其實我是在寫這篇文章的時候才發現了上面的語法,以前我是用了另一種比較 dirty 的方式去作的:
enum CustomOptional<Wrapped> {
case value(Wrapped)
case none
static func `init`<Wrapped>(value: Wrapped) -> CustomOptional<Wrapped> {
return .value(value)
}
}複製代碼
很早的時候我就嘗試過定義一個名爲 init
的 static 函數,獲得的是這樣的提示 error: keyword 'init' cannot be used as an identifier here
,也就是說 init 做爲系統關鍵字不能在這裏使用,那麼很簡單,用 ` 把它包住就好了。
這麼定義 init
方法的話,在調用時也能夠像正常的構造器那樣省略掉 init
:
let _ = CustomOptional(value: "I'm a String")複製代碼
這種「構造器」的定義和實現都很靈活,能夠返回任何類型,內部實現也不須要遵照那麼多規則。這可能在一些我意想不到的場景下會有用吧,但我暫時沒有想到,若是你剛好用到了這個小技巧,請務必發個郵件告訴我,我很好奇具體的使用場景。
出處:kemchenj
定義值類型的時候,同一個函數,咱們常常須要定義 mutating 和 non-mutating 兩個版本:
func sorted() -> Array { ... }
mutating func sort() { ... }複製代碼
但絕大部分狀況下這兩個函數的實現基本上都是同樣的,這個時候咱們就能夠考慮複用其中一個,減小重複代碼:
func sorted() -> Array { ... }
mutating func sort() {
self = sorted()
}複製代碼
之因此能夠這樣寫,是由於 mutating 意味着函數會對值自身進行修改:
self.property = value
// 等價於
var newStruct = self
newStruct.property = value
self = newStruct複製代碼
我想推薦一下這個視頻,主要是講 Swift 裏如何構建高效的 Collection 類型,20分鐘的長度,看完以後對於 objc.io 的那本書動心了,我基礎不好也基本上看懂了裏面的內容,講得真的很不錯,裏面平衡二叉樹的實現讓我再一次強烈地感覺到 Swift 的簡潔。
以爲文章還不錯的話能夠關注一下個人博客