亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Swift 使用 Option Pattern 如何改善可選項的 API 設計

發布時間:2020-10-26 15:26:31 來源:億速云 閱讀:148 作者:Leah 欄目:開發技術

Swift 使用 Option Pattern 如何改善可選項的 API 設計?相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

SwiftUI 中提供了很多“新穎”的 API 設計思路和 Swift 的使用方式,我們可以進行借鑒,并反過來使用到普通的 Swift 代碼中。PreferenceKey 的處理方式就是其中之一:它通過 protocol 的方式,為子 view 們提供了一套模式,讓它們能將自定義值以類型安全的方式,向上傳到父 view 去。如果有機會,我會再專門介紹 PreferenceKey,但這種設計的模式其實和 UI 無關,在一般的 Swift 里,我們也能使用這種方法來改善 API 設計。

在這篇文章里,我們就來看看要如何做。文中相關的代碼可以在這里找到。你可以將這些代碼復制到 Playground 中執行并查看結果。

紅綠燈

用一個交通信號燈作為例子。

Swift 使用 Option Pattern 如何改善可選項的 API 設計

作為 Model 類型的 TrafficLight 類型定義了 .stop.proceed .caution 三種 State,它們分別代表停止、通行和注意三種狀態 (當然,通俗來說就是“紅綠黃”,但是 Model 不應該和顏色,也就是 View 層級相關)。它還持有一個 state 來表示當前的狀態,并在設置時將這個狀態通過 onStateChanged 發送出去:

public class TrafficLight {

  public enum State {
    case stop
    case proceed
    case caution
  }

  public private(set) var state: State = .stop {
    didSet { onStateChanged?(state) }
  }
  
  public var onStateChanged: ((State) -> Void)?
}

其余部分的邏輯和本次主題無關,不過它們也比較簡單。如果你有興趣的話,可以點開下面的詳情查看。但這不影響本文的理解。

TrafficLight 的其他部分

在 (ViewController 中) 使用這個紅綠燈也很簡單。我們按照紅綠黃的顏色,在 onStateChanged 中設定 view 的顏色:

light = TrafficLight()
light.onStateChanged = { [weak self] state in
  guard let self = self else { return }
  let color: UIColor
  switch state {
  case .proceed: color = .green
  case .caution: color = .yellow
  case .stop: color = .red
  }
  UIView.animate(withDuration: 0.25) {
    self.view.backgroundColor = color
  }
}
light.start()

這樣,View 的顏色就可以隨著 TrafficLight 的變化而變更了:

Swift 使用 Option Pattern 如何改善可選項的 API 設計

青色信號

世界很大,有些地方 (比如日本) 會使用傾向于青色,或者實際上應該是綠松色 (turquoise),來表示“可以通行”。有時候這也是技術的限制或者進步所帶來的結果。

The green light was traditionally green in colour (hence its name) though modern LED green lights are turquoise.

– Wikipedia 中關于 Traffic light 的記述

Swift 使用 Option Pattern 如何改善可選項的 API 設計

假設我們想要讓 TrafficLight 支持青色的綠燈,一個能想到的最簡單的方式,就是在 TrafficLight 里為“綠燈顏色”提供一個選項:

public class TrafficLight {
  public enum GreenLightColor {
    case green
    case turquoise
  }
  public var preferredGreenLightColor: GreenLightColor = .green
  
  //...
}

然后在 ViewController 中使用對應的顏色:

extension TrafficLight.GreenLightColor {
  var color: UIColor {
    switch self {
    case .green: 
      return .green
    case .turquoise: 
      return UIColor(red: 0.25, green: 0.88, blue: 0.82, alpha: 1.00)
    }
  }
}

light.preferredGreenLightColor = .turquoise
light.onStateChanged = { [weak self, weak light] state in
  guard let self = self, let light = light else { return }
  // ...
  
  // case .proceed: color = .green
  case .proceed: color = light.preferredGreenLightColor.color
}

這樣做當然能夠解決問題,但是也會帶來一些隱患。首先,需要在 TrafficLight 中添加一個額外的存儲屬性 preferredGreenLightColor,這使得 TrafficLight 示例所使用的內存開銷增加了。在上例中,額外的 GreenLightColor 屬性將會為每個實例帶來 8 byte 的開銷。 如果我們需要同時處理很多 TrafficLight 實例,而其中只有很少數需要 .turquoise 的話,這個開銷就非常可惜了。

嚴格來說,上例的 TrafficLight.GreenLightColor 枚舉其實只需要占用 1 byte。但是 64-bit 系統中在內存分配中的最小單位是 8 bytes。

如果想要添加的屬性不是像例子中這樣簡單的 enum,而是更加復雜的帶有多個屬性的類型的話,這一開銷會更大。

另外,如果我們還要添加其他屬性,很容易想到的方法是繼續在 TrafficLight 上加入更多的存儲屬性。這其實是很沒有擴展性的方法,我們并不能在 extension 中添加存儲屬性:

// 無法編譯
extension TrafficLight {
  enum A {
    case a
  }
  var myOption: A = .a // Extensions must not contain stored properties
}

需要修改 TrafficLight 的源碼,才能添加這個選項,而且還需要為添加的屬性設置合適的初始值,或者提供額外的 init 方法。如果我們不能直接修改 TrafficLight 的源碼 (比如這個類型是別人的代碼,或者是被封裝到 framework 里的),那么像這樣的添加選項的方式其實是無法實現的。

Option Pattern

可以用 Option Pattern 來解決這個問題。在 TrafficLight 中,我們不去提供專用的 preferredGreenLightColor,而是定義一個泛用的 options 字典,來將需要的選項值放到里面。為了限定能放進字典中的值,新建一個 TrafficLightOption 協議:

public protocol TrafficLightOption {
  associatedtype Value

  /// 默認的選項值
  static var defaultValue: Value { get }
}

TrafficLight 中,加入下面的 options 屬性和下標方法:

public class TrafficLight {

  // ...

  // 1
  private var options = [ObjectIdentifier: Any]()

  public subscript<T: TrafficLightOption>(option type: T.Type) -> T.Value {
    get {
      // 2
      options[ObjectIdentifier(type)] as&#63; T.Value
        &#63;&#63; type.defaultValue
    }
    set {
      options[ObjectIdentifier(type)] = newValue
    }
  }
  
  // ...  
}
  1. 只有滿足 Hashable 的類型,才能作為 options 字典的 key。ObjectIdentifier 通過給定的類型或者是 class 實例,可以生成一個唯一代表該類型和實例的值。它非常適合用來當作 options 的 key。
  2. 通過 key 在 options 中尋找設置的值。如果沒有找到的話,返回默認值 type.defaultValue

現在,對 TrafficLight.GreenLightColor 進行擴展,讓它滿足 TrafficLightOption。如果 TrafficLight 已經被打包成 framework,我們甚至可以把這部分代碼從 TrafficLight 所在的 target 中拿出來:

extension TrafficLight {
  public enum GreenLightColor: TrafficLightOption {
    case green
    case turquoise

    public static let defaultValue: GreenLightColor = .green
  }
}

我們將 defaultValue 聲明為了 GreenLightColor 類型,這樣TrafficLightOption.Value 的類型也將被編譯器推斷為 GreenLightColor

最后,為這個選項提供 setter 和 getter:

extension TrafficLight {
  public var preferredGreenLightColor: TrafficLight.GreenLightColor {
    get { self[option: GreenLightColor.self] }
    set { self[option: GreenLightColor.self] = newValue }
  }
}

現在,你可以像之前那樣,通過直接在 light 上設置 preferredGreenLightColor 來使用這個選項,而且它已經不是 TrafficLight 的存儲屬性了。只要不進行設置,它便不會帶來額外的開銷。

light.preferredGreenLightColor = .turquoise

有了 TrafficLightOption,現在想要為 TrafficLight 添加選項時,就不需要對類型本身的代碼進行改動了,我們只需要聲明一個滿足 TrafficLightOption 的新類型,然后為它實現合適的計算屬性就可以了。這大幅增加了原來類型的可擴展性。

總結

Option Pattern 是一種受到 SwiftUI 的啟發的模式,它幫助我們在不添加存儲屬性的前提下,提供了一種向已有類型中以類型安全的方式添加“存儲”的手段。

這種模式非常適合從外界對已有的類型進行功能上的添加,或者是自下而上地對類型的使用方式進行改造。這項技術可以對 Swift 開發和 API 設計的更新產生一定有益的影響。反過來,了解這種模式,相信對于理解 SwiftUI 中的很多概念,比如 PreferenceKey alignmentGuide 等,也會有所助益。

看完上述內容,你們掌握Swift 使用 Option Pattern 如何改善可選項的 API 設計的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

页游| 沙雅县| 平罗县| 桐庐县| 克东县| 滨海县| 万荣县| 九寨沟县| 沙湾县| 和林格尔县| 车险| 潢川县| 南昌市| 九寨沟县| 托里县| 呼玛县| 永平县| 廊坊市| 乡宁县| 新安县| 宁津县| 西华县| 准格尔旗| 江山市| 寿阳县| 突泉县| 台东县| 潼关县| 田阳县| 雅江县| 台北市| 石门县| 来安县| 延吉市| 汾西县| 潼南县| 枞阳县| 大悟县| 云龙县| 衡南县| 陆良县|