web-dev-qa-db-ja.com

なぜ列挙型には計算されたプロパティがありますが、Swiftに保存されたプロパティはありませんか?

私はSwiftが初めてで、ドキュメントでこれに出くわしました:

計算されたプロパティは、クラス、構造、および列挙によって提供されます。格納されたプロパティは、クラスと構造体によってのみ提供されます。

何故ですか?列挙型に関連付けられた値は、保存されたプロパティのように機能しますか?彼らは最初にプロパティを保存していたようです- なぜSwiftのクラスに保存されたタイププロパティがないのですか?

26
Adit Gupta

enumsにはtypeプロパティが保存されています-つまり、staticプロパティ。 instanceプロパティが保存されていません。 enumsに格納されたインスタンスプロパティが利用できない技術的な理由があるかどうかはわかりません。 「理由」の技術的な回答が必要な場合は、devフォーラムで質問する必要があります。

質問では、関連付けられた値が保存されたプロパティのように機能するかどうかを尋ねます。実際、それらはstructsおよびclassesの保存されたプロパティよりも柔軟性があり、(ある意味では)より柔軟です。 case内の各enumは、それに関連付けられた専用のデータセットを持つことができます。すべてのcasesに適用される1組の保存済みプロパティがあるのではなく、_caseごとに保存済みプロパティを個別化することができます。

15
Aaron Rasmussen

格納されたインスタンスプロパティを許可しない列挙型は、設計上の選択です。格納されたインスタンスプロパティを持つ列挙型は、構造体(列挙型の超能力を持つ)のようになりますが、型の観点から見ると、列挙型は型の乗数のように機能します。基本的に考慮する

enum Set1 {
    case a
    case b
    case c
}

enum Times {
    case x
    case y

    var k: Set1
}

これが実際に意味するのは、enum Timesを使用すると、Set1Set2の要素を任意に組み合わせて6つの異なるケースが得られることですが、待って、これがTupleの目的であることを知っています(Set1, Set2)のように入力します。Timesは次のように宣言できます

typealias Times = (Set1, Set2)

これが前者のケースを許可しない合理的な根拠として役立つことを願っています。

つまり、Swift=列挙型を使用すると、任意のn-Tupleを各caseに関連付けることができます。必要に応じて、ケースにアタッチされたストアドプロパティです。タイプの観点からは、タイプアダーとして機能するようになりました。

enum Add {
    case lhs(Set1)
    case rhs(Set2)
}

現在、5つの異なるケースがあります。 2タプルを保存する場合:

enum AddTimes {
    case lhs(Set1, Set2)
    case rhs(Set3, Set4)
}

これで基本的に乗算の合計が得られました(Set1 * Set2 + Set3 * Set4)。これは、パターンマッチングに関して非常に強力なツールです。

[〜#〜] however [〜#〜]、enum内に格納されたプロパティの形式を実際にエミュレートしたい場合があります。このことを考慮:

public enum Service {
    case registerNewUser(username: String, password: String, language: String)
    case login(username: String, password: String, deviceTokenº: String?)
    case logout(sessionToken: String)
    case sendForgotPassword(email: String)
}

REST=エンドポイント(Moyaのようなフレームワークで)を定義する宣言的な方法です)

MoyaProvider<Service>.request(.sendForgotPassword(email: "[email protected]"))

しかし、本番サーバーとテストサーバーを区別したいと想像してください。それぞれの場合に別のTuple要素としてサーバーを追加する場合:

case forgotPassword(sessionToken: String, serverBaseURLString: String)

元々各タプルはリクエストパラメータを保存するつもりでしたが、今ではサーバーベースアドレスを保存するため、これは間違ったセマンティクスを持ちます。

このようなことを避けるために、次の方法で実際に型をパラメータ化できます。サーバーを次のように定義する代わりに:

enum Server: String {
    case production = "https://service.info"
    case test = "http://test.service.info"
}

次のように、各ケースに個別のタイプで定義できます。

public struct ProductionServer: ServerType {
    public static var baseURLString: String { return "https://service.info" }
}
public struct TestServer: ServerType {
    public static var baseURLString: String { return  "http://test.service.info" }
}
public protocol ServerType {
    static var baseURLString: String { get }
}

最後に、ServiceTypeを次のようにパラメーター化します。

public enum Service<T> where T: ServerType {
    case registerNewUser(username: String, password: String, language: String)
    case login(username: String, password: String, deviceTokenº: String?)
    case logout(sessionToken: String)
    case sendForgotPassword(email: String)

    var serverURL: URL {
        return T.baseURL
    }
}

public typealias ProdutionService = Service<ProductionServer>
public typealias TestService = Service<TestServer>
13
ambientlight

初期化時にアクセスできないプロパティを保存するために、小さなトリックを使用します。

最初に、保存されたプロパティを将来保存するクラスFutureを作成します。

class Future<T> {
  var value: T?
  init(value: T? = nil) {
      self.value = value
  }
}

次に、列挙型を作成します。

enum Sample {
  case Test(future: Future<String>)
}

インスタンス化:

let enumTest = Sample.Test(future: Future())

コードの後半:

switch enumTest {
  case let .Test(future):
  future.value = "Foo"
}

後で、値にアクセスできます。

switch enumTest {
  case let .Test(future):
  // it will print Optional("Foo")
  print("\(future.value)")
}

これは悪用すべきものではありませんが、場合によっては役立ちます。

それが役に立てば幸い。

4
manueGE

列挙型は、コード内でStringやIntなどを何度も変更することなく変更できる構造化データ型と見なされ、列挙型を使用すると、同じことを何度も変更する必要がなくなります。たとえば、ドロップダウンメニュー:

enum DropDownMenuOptions: String {
  case MenuOptionOne
  case MenuOptionTwo
  case MenuOptionThree
}

保存されたプロパティを使用して、uは必要な情報を事前計算し、メイン関数のコードを削減できます。最良の例は、たとえば長方形のサイズを計算することです:

struct Point {
    var x = 0.0, y = 0.0
}
struct Size {
    var width = 0.0, height = 0.0
}
struct Rect {
    var Origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = Origin.x + (size.width / 2)
            let centerY = Origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set(newCenter) {
            Origin.x = newCenter.x - (size.width / 2)
            Origin.y = newCenter.y - (size.height / 2)
        }
    }
}

var square = Rect(Origin: Point(x: 0.0, y: 0.0),
    size: Size(width: 10.0, height: 10.0))
0