通过这个简单的类,我得到了编译器警告
试图在自己的setter/getter中修改/访问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
}
}
// ...
}
Swift中的setter /getter与ObjC有很大不同。属性变成一个计算属性,这意味着它不像在ObjC中那样有一个支持变量,比如_x。
在下面的解决方案代码中,您可以看到xTimesTwo不存储任何东西,而是简单地计算x的结果。
请参阅有关计算属性的官方文档。
您想要的功能也可能是Property observer。
你需要的是:
var x: Int
var xTimesTwo: Int {
set {
x = newValue / 2
}
get {
return x * 2
}
}
您可以在setter/getter中修改其他属性,这就是它们的意义所在。
setter和getter应用于计算属性;这样的属性在实例中没有存储空间——getter中的值将从其他实例属性中计算出来。在你的例子中,没有x被赋值。
显式地:“在没有显式支持ivars的情况下如何做到这一点”。您不能这样做—您需要备份计算的属性。试试这个:
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
详细解释一下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)
}
}
}
注意,center computed属性在变量的声明中从不修改或返回自身。
你用x递归地定义x,就好像有人问你多大了?你回答"我的年龄是我的两倍"这毫无意义。
你必须说我的年龄是约翰的两倍或者其他变量,除了你。
计算变量总是依赖于另一个变量。
经验法则是永远不要从getter (get)中访问属性本身。因为这会引发另一个get,而这个get又会引发另一个get…甚至不要打印出来。因为打印也需要在打印之前“获取”值!
struct Person{
var name: String{
get{
print(name) // DON'T do this!!!!
return "as"
}
set{
}
}
}
let p1 = Person()
因为这将给出以下警告:
试图从它自己的getter中访问'name'。
错误看起来像这样模糊:
作为替代,你可能想要使用didSet。使用didSet,你会得到一个值,这个值是之前设置的,刚刚设置的。更多信息请看这个答案。
更新: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")
Getter
print(triangle.perimeter) // invoking getter
把
triangle.perimeter = 9.9 // invoking setter