属性获取器和设置器

Posted

技术标签:

【中文标题】属性获取器和设置器【英文标题】:Property getters and setters 【发布时间】:2014-07-24 09:20:42 【问题描述】:

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

试图在自己的 setter/getter 中修改/访问x

当我这样使用它时:

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

我得到一个 EXC_BAD_ACCESS。在没有明确支持 ivars 的情况下如何做到这一点?

class Point 

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

【问题讨论】:

这样做也会给编译器带来额外的负担,并且会大量消耗 CPU。我就是这么做的:| . This 是我创建的错误。 (我正在使用游乐场) 这种行为令人困惑,而不是使用set 你想使用didSet。当您实现set 时,属性在 Swift 中的行为与 Objective-C 或其他语言不同。请参阅 answer below from @jack 和 example of didSet from @cSquirrel 【参考方案1】:

我不知道这是否是一种好习惯,但您可以这样做:

class test_ancestor 
    var prop: Int = 0



class test: test_ancestor 
    override var prop: Int 
        get 
            return super.prop // reaching ancestor prop
        
        set 
            super.prop = newValue * 2
        
    


var test_instance = test()
test_instance.prop = 10

print(test_instance.prop) // 20

Read more

【讨论】:

【参考方案2】:

更新: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

【讨论】:

【参考方案3】:

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

class Point 

var x: Int 
    didSet 
        x = x * 2
    

...

至于吸气剂...

class Point 

var doubleX: Int 
    get 
        return x / 2
    

...

【讨论】:

你怎么能用这个模式中的默认值初始化x 我明白了,应该是:var x: Int = 0 didSet ...【参考方案4】:

Swift 中的 Setter/getter 与 ObjC 完全不同。该属性变成了一个计算属性,这意味着它确实没有像在 ObjC 中那样有一个支持变量,例如 _x

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

见Official docs on computed properties。

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

你需要的是:

var x: Int

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

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

【讨论】:

是的,这就是我在文档中阅读的内容。我跳过了属性部分,似乎没有办法解决这个问题,对吧? 是的,如果你真的想这样做,你将需要另一个实例变量来“支持”第一个。但是,setter 并不是为此目的而设计的,您可能应该重新考虑您要通过此实现的目标。 您可以使用didSet,它允许您在设置后立即更改该值。没有什么可以得到的...... 注意:如果您想恢复该值,因为它是无效输入,您需要在didSet 中将x 设置回oldValue。来自 Objective-C 属性的这种行为变化非常令人困惑,谢谢!【参考方案5】:

Swift 5.1 更新

从 Swift 5.1 开始,您现在可以在不使用 get 关键字的情况下获取变量。例如:

var helloWorld: String 
"Hello World"

【讨论】:

如果我尝试更改,helloWorld = "macWorld",无法赋值:'helloWorld' is a get-only property error is shown. 是否有可能分配新值?还是没有? 我认为不可能。 从字面上看,var helloWorld: String "Hello World" 和 let helloWorld: String = "Hello World" 是一样的吗? 是的,我想是的。【参考方案6】:

这是一个理论上的答案。可以找到here

get set 属性不能是常量存储属性。它应该是一个计算属性,并且 get 和 set 都应该实现。

【讨论】:

【参考方案7】:

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

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

计算变量总是依赖于另一个变量。


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

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


let p1 = Person()

因为这会给出以下警告:

试图从它自己的 getter 中访问 'name'。

这个错误看起来很模糊:

作为替代方案,您可能想要使用didSet。使用didSet,您将保留之前设置的值,然后设置为。更多信息请见this answer。

【讨论】:

【参考方案8】:

Setters 和 Getters 申请computed properties;此类属性在实例中没有存储空间 - 来自 getter 的值旨在从其他实例属性中计算得出。在您的情况下,没有要分配的 x

明确地:“没有明确的支持 ivars 我怎么能做到这一点”。你不能 - 你需要 something 来备份计算的属性。试试这个:

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

【讨论】:

【参考方案9】:

要覆盖 settergetter 的 swift 变量,请使用以下代码

var temX : Int? 
var x: Int?

    set(newX)
       temX = newX
    

    get
        return temX
    

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

我们可以像这样简单地调用setter

x = 10

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

var newVar = x

【讨论】:

【参考方案10】:

试试这个:

var x:Int!

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

这基本上是 Jack Wu 的答案,但不同的是,在 Jack Wu 的答案中,他的 x 变量是 var x: Int,在我的中,我的 x 变量是这样的:var x: Int!,所以我所做的只是将其设为可选类型。

【讨论】:

【参考方案11】:

详细说明 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 的 Playground 环境的右下角看到微调器)。

考虑Swift documentation中的示例:

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 ,这会触发另一个 . . .甚至不要打印它。因为打印还需要“获取”值才能打印!【参考方案12】:

Swift 中的 Setter 和 getter 适用于计算属性/变量。这些属性/变量实际上并不存储在内存中,而是根据存储的属性/变量的值计算得出的。

请参阅 Apple 关于该主题的 Swift 文档:Swift Variable Declarations。

【讨论】:

以上是关于属性获取器和设置器的主要内容,如果未能解决你的问题,请参考以下文章

PFObject 子类获取器和设置器?

选择器和层叠

PyQt5 获取器和设置器

Javascript:对象设置器和获取器在克隆/复制/扩展后丢失

CSS 属性选择器和区分大小写的“类型”属性与虚构属性

CSS选择器和属性