Swift 冷門語法知識

原本這篇文章的標題是「如何寫一個不安全的構造器」,但後面查資料的時候又發現了一些很好玩的東西,就一次性寫成一篇出來,跟你們分享一下 Swift 裏的幾個 best pratice:git

  • 帶關聯值的 Enum 的構造器
  • strongSelf 的另外一種寫法
  • 如何在 Swift 裏寫一個不安全的構造器
  • mutating 函數的定義

帶關聯值的 Enum 的構造器

寫 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閉包

strongSelf 的另外一種寫法

以前我就寫過一篇文章來說這個,之因此再提一次,一方面是爲了文章的完整性,另外一方面就是爲了下文的另外一個語法作鋪墊。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 所有都改成 strongSelfcode

出處:忘了😒

Update 2017.08.24:

感謝大神在評論裏提醒我,原來這是一個編譯的 bug,提案 SE-0079 很詳細地講了這件事情,但目前這個 bug 尚未修復,按照上面的方法去寫就能夠了。

若是這個 bug 被修復了的話,就能夠不必加上 `,能夠直接聲明局部變量 self:

block = { [weak self] in
    guard let self = self else { return }
    ... other code ...
}複製代碼

如何在 Swift 裏寫一個不安全的構造器

開頭我提到了這篇文章本來的標題是叫作「如何寫一個不安全的構造器」,其實我是在寫這篇文章的時候才發現了上面的語法,以前我是用了另一種比較 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 函數的定義

定義值類型的時候,同一個函數,咱們常常須要定義 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 Talk #21 Structs and Mutation

最後

我想推薦一下這個視頻,主要是講 Swift 裏如何構建高效的 Collection 類型,20分鐘的長度,看完以後對於 objc.io 的那本書動心了,我基礎不好也基本上看懂了裏面的內容,講得真的很不錯,裏面平衡二叉樹的實現讓我再一次強烈地感覺到 Swift 的簡潔。

以爲文章還不錯的話能夠關注一下個人博客

相關文章
相關標籤/搜索