プロパティのゲッターとセッター


194

この単純なクラスでは、コンパイラの警告が表示されます

x独自のセッター/ゲッター内で変更/アクセスを試みる

そして私がこのようにそれを使うとき:

var p: point = Point()
p.x = 12

EXC_BAD_ACCESSを取得します。明示的なバッキングivarなしでこれを行うにはどうすればよいですか?

class Point {

    var x: Int {
        set {
            x = newValue * 2 //Error
        }
        get {
            return x / 2 //Error
        }
    }
    // ...
}

1
これを行うと、コンパイラーに余分な負荷がかかり、CPUが激しく消費されます。私はそれをやった:| 。これは私が作成していたエラーでした。(私は遊び場を使用していました)
ハニー

この動作は、setあなたがを使用したいのではなく、混乱を招きますdidSet。Swiftでのプロパティの動作は、Objective-Cや他の言語とは異なりますset@jackからの回答@cSquirrelからの例をdidSet
Paul Solt

回答:


232

セッターとゲッターはに適用されcomputed propertiesます。そのようなプロパティはインスタンスにストレージを持ちません-ゲッターからの値は他のインスタンスプロパティから計算されることを意図しています。あなたの場合、x割り当てられるものはありません。

明示的に:「明示的なバッキングivarなしでこれを行うにはどうすればよいですか」。できません- 計算されたプロパティをバックアップするために何かが必要になります。これを試して:

class Point {
  private var _x: Int = 0             // _x -> backingX
  var x: Int {
    set { _x = 2 * newValue }
    get { return _x / 2 }
  }
}

特に、Swift REPLでは:

 15> var pt = Point()
pt: Point = {
  _x = 0
}
 16> pt.x = 10
 17> pt
$R3: Point = {
  _x = 20
}
 18> pt.x
$R4: Int = 10

106

Swiftのセッター/ゲッターはObjCとはかなり異なります。プロパティは計算されたプロパティになり、ObjCのようにバッキング変数がないことを意味し_xます。

以下のソリューションコードでは、xTimesTwoは何も保存せ、単にからの結果を計算しているだけxです。

計算されたプロパティに関する公式ドキュメントをご覧ください。

必要な機能は、プロパティオブザーバーかもしれませ

必要なものは:

var x: Int

var xTimesTwo: Int {
    set {
       x = newValue / 2
    }
    get {
        return x * 2
    }
}

セッター/ゲッター内の他のプロパティを変更できます。


1
はい、それは私がドキュメントで読んでいるものです。プロパティセクションをスキップしましたが、これを回避する方法がないようですよね?
Atomix 2014年

はい、本当にこれを実行したい場合は、最初のインスタンス変数を「戻す」ために別のインスタンス変数が必要になります。ただし、セッターはこの目的のためのものではありません。これを使用して何を達成しようとしているのかを再考する必要があります。
ジャック

5
あなたは使うことができdidSetますが、それが設定された直後の値を変更することができるようになります。ただし、取得するものは何もありません...
ObjectiveCsam

1
注:あなたはそれが無効で入力されたため、値を戻したい場合は、セットに必要があると思いxに戻っoldValueの中でdidSet。この動作の変更は、Objective-Cのプロパティによるもので、非常に混乱しています。
ポールソルト2018

105

プロパティオブザーバーを使用して設定値をカスタマイズできます。これを行うには、「セット」の代わりに「didSet」を使用します。

class Point {

var x: Int {
    didSet {
        x = x * 2
    }
}
...

ゲッターは...

class Point {

var doubleX: Int {
    get {
        return x / 2
    }
}
...

xこのパターンのデフォルト値でどのように初期化できますか?
i_am_jorf

3
なるほどvar x: Int = 0 { didSet { ...
i_am_jorf

31

GoZonerの答えを詳しく説明するには:

ここでの実際の問題は、ゲッターを再帰的に呼び出すことです。

var x:Int
    {
        set
        {
            x = newValue * 2 // This isn't a problem
        }
        get {
            return x / 2 // Here is your real issue, you are recursively calling 
                         // your x property's getter
        }
    }

上記のコードコメントのように、xプロパティのゲッターを無限に呼び出します。これは、EXC_BAD_ACCESSコードを取得するまで実行され続けます(Xcodeのプレイグラウンド環境の右下隅にスピナーが表示されます)。

Swiftのドキュメントの例を考えてみます

struct Point {
    var x = 0.0, y = 0.0
}
struct Size {
    var width = 0.0, height = 0.0
}
struct AlternativeRect {
    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 {
            origin.x = newValue.x - (size.width / 2)
            origin.y = newValue.y - (size.height / 2)
        }
    }
}

中心の計算されたプロパティが変数の宣言で変更または返されないことに注意してください。


経験則では、getter ie からプロパティ自体にアクセスすることはありません。それは別のものを誘発する別のものを誘発するからです。。。印刷もしないでください。印刷では、値を印刷する前に値を「取得」する必要があるためです。getget
Honey

19

オーバーライドsettergetterて迅速な変数のために、以下のコードを使用してください

var temX : Int? 
var x: Int?{

    set(newX){
       temX = newX
    }

    get{
        return temX
    }
}

ゲッター/セッターがオーバーライドされている同じ変数にアクセスしようとすると、無限ループが発生するため、変数の値を一時変数に保持する必要があります。

このように簡単にセッターを呼び出すことができます

x = 10

ゲッターは、指定されたコード行の下での発火時に呼び出されます

var newVar = x

8

あなたは再帰的に定義xしていますxます。誰かがあなたに何歳かと尋ねたかのように?そして、あなたは「私は私の年齢の2倍です」と答えます。それは無意味です。

私はジョンの年齢の2倍か他の変数であると言わなければなりませんが、自分でます。

計算された変数は常に別の変数に依存しています。


経験則では、getter ie からプロパティ自体にアクセスすることはありません。それは別のものを誘発する別のものを誘発するからです。。。印刷もしないでください。印刷では、値を印刷する前に値を「取得」する必要があるためです。getget

struct Person{
    var name: String{
        get{
            print(name) // DON'T do this!!!!
            return "as"
        }
        set{
        }
    }
}

let p1 = Person()

それは次の警告を与えるでしょう:

自身のゲッター内から 'name'にアクセスしようとしています。

エラーはこのように曖昧に見えます:

ここに画像の説明を入力してください

別の方法として、使用することもできますdidSet。これでdidSet、以前に設定されたばかりの値が保持されます。詳細については、この回答を参照してください。


8

アップデート:Swift 4

以下のクラスでは、セッターとゲッターが変数に適用されます sideLength

class Triangle: {
    var sideLength: Double = 0.0

    init(sideLength: Double, name: String) { //initializer method
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 3
    }

    var perimeter: Double {
        get { // getter
            return 3.0 * sideLength
        }
        set(newValue) { //setter
            sideLength = newValue / 4.0
        }
   }

オブジェクトを作成しています

var triangle = Triangle(sideLength: 3.9, name: "a triangle")

ゲッター

print(triangle.perimeter) // invoking getter

セッター

triangle.perimeter = 9.9 // invoking setter

6

これを使ってみてください:

var x:Int!

var xTimesTwo:Int {
    get {
        return x * 2
    }
    set {
        x = newValue / 2
    }
}

これは基本的にジャックウーの答えですが、違いはジャックウーの答えで彼のx変数がvar x: Intであるということです。私の場合、私のx変数は次のようにvar x: Int!なります。


1

Swift 5.1のアップデート

Swift 5.1以降、getキーワードを使用せずに変数を取得できるようになりました。例えば:

var helloWorld: String {
"Hello World"
}

変更しようとすると、helloWorld = "macWorld"、値に割り当てられません: 'helloWorld'は取得専用のプロパティエラーです。
McDonal_11

新しい値を割り当てることは可能ですか?またはいいえ?
McDonal_11

可能だとは思いません。
atalayasa

文字通り、var helloWorld:String {"Hello World"}そして、helloWorld:String = "Hello World"は同じですか?
McDonal_11

はい、そう思います。
atalayasa

0

Swiftのセッターとゲッターは、計算されたプロパティ/変数に適用されます。これらのプロパティ/変数は実際にはメモリに保存されず、保存されたプロパティ/変数の値に基づいて計算されます。

この件に関するAppleのSwiftのドキュメントを参照してください:Swift変数宣言


0

これが理論的な答えです。それはここにあります

{get set}プロパティは、定数に格納されたプロパティであってはなりません。これは計算されたプロパティである必要があり、getとsetの両方を実装する必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.