财产获取者和安置者


194

通过这个简单的类,我得到了编译器警告

尝试x在自己的设置器/ 获取器中修改/访问

当我这样使用它时:

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

我得到一个EXC_BAD_ACCESS。没有明确的支持,我该怎么办?

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

Answers:


232

Setter和Getters适用于computed properties;这样的属性在实例中没有存储-来自getter的值应从其他实例属性中计算出来。在您的情况下,没有x要分配的内容。

明确地:“如何在没有明确支持的情况下做到这一点”。您不能-您将需要一些内容来备份计算的属性。试试这个:

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完全不同。该属性变成了计算的属性,这意味着它具有背衬变量如_x因为它会在ObjC。

在下面的解决方案代码中,您可以看到xTimesTwo没有存储任何内容,而只是从计算结果x

请参阅有关计算属性的官方文档

您想要的功能也可能是Property Observers

您需要的是:

var x: Int

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

您可以在setter / getter中修改其他属性,这就是它们的含义。


1
是的,这就是我在文档中阅读的内容。我跳过了属性部分,似乎没有办法解决这个问题,对吗?
Atomix 2014年

是的,如果您确实想执行此操作,则需要另一个实例变量来“备份”第一个变量。但是,setter并非旨在达到此目的,您可能应该重新考虑您要完成的工作。
杰克

5
您可以使用didSet它来设置值后立即更改它。没事了……
ObjectiveCsam

1
注意:如果要恢复的价值,因为它是无效的输入,你需要一套xoldValuedidSet。行为上的这种变化非常令人困惑,这是由Objective-C属性引起的,谢谢!
保罗·索尔特

105

您可以使用属性观察器来自定义设置值。为此,请使用“ didSet”而不是“ set”。

class Point {

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

至于吸气剂...

class Point {

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

如何x在此模式下使用默认值初始化?
i_am_jorf '16

3
我知道的是:var x: Int = 0 { didSet { ...
i_am_jorf '16

31

详细阐述GoZoner的答案:

您真正的问题是,您递归地调用您的getter。

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属性的getter,它将继续执行直到获得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 访问属性本身,即get。因为那会触发另一个get,这会触发另一个。。。甚至不要打印它。因为打印还需要“获取”值才能打印出来!
亲爱的

19

为了覆盖settergetter快速变量使用下面的给定代码

var temX : Int? 
var x: Int?{

    set(newX){
       temX = newX
    }

    get{
        return temX
    }
}

我们需要将变量的值保留在一个临时变量中,因为尝试访问被getter / setter覆盖的同一变量将导致无限循环。

我们可以像这样简单地调用设置器

x = 10

在给定的代码行以下触发时将调用Getter

var newVar = x

8

你是递归定义xx。好像有人问你你几岁?然后您回答“我的年龄是我的两倍”。这是没有意义的。

您必须说我的年龄是约翰的两倍,或其他任何变量,你自己。

计算变量始终依赖于另一个变量。


经验法则是永远不要从getter 访问属性本身,即get。因为那会触发另一个get,这会触发另一个。。。甚至不要打印它。因为打印还需要“获取”值才能打印出来!

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

let p1 = Person()

这样会发出以下警告:

尝试从自己的获取器中访问“名称”。

该错误看起来像这样模糊:

在此处输入图片说明

作为替代方案,您可能要使用didSet。这样didSet一来,您就可以保留之前设置的值和刚刚设置的值。有关更多信息,请参见此答案


8

更新:Swift 4

在下面的类中,将setter和getter应用于变量 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
    }
}

基本上,这是Jack Wu的答案,但是区别在于,在Jack Jack的答案中,他的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_19年

有可能分配新值吗?或没有 ?
McDonal_19年

我认为不可能。
atalayasa

那么从字面上看,var helloWorld:String {“ Hello World”}和,让helloWorld:String =“ Hello World”相同吗?
McDonal_19年

是的,我想是这样。
atalayasa

0

Swift中的setter和getter应用于计算的属性/变量。这些属性/变量实际上并未存储在内存中,而是根据存储的属性/变量的值进行计算。

请参阅Apple的Swift文档,主题为:Swift变量声明


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.