221

With this simple class I am getting the compiler warning

Attempting to modify/access x within its own setter/getter

and when I use it like this:

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

I get an EXC_BAD_ACCESS. How can I do this without explicit backing ivars?

class Point {

    var x: Int {
        set {
            x = newValue * 2 //Error
        }
        get {
            return x / 2 //Error
        }
    }
    // ...
}
mfaani
  • 33,269
  • 19
  • 164
  • 293
Atomix
  • 13,427
  • 9
  • 38
  • 46
  • 1
    Doing such would also put extra load on the compiler and would consume CPU vigorously. I just did that :| . [This](https://forums.developer.apple.com/thread/49444) was the error I was creating. ( I was using playground) – mfaani Nov 26 '16 at 17:48
  • 1
    This behavior is confusing, instead of using `set` you want to be using `didSet`. Properties behave differently in Swift than Objective-C or other languages when you implement `set`. See the [answer below from @jack](https://stackoverflow.com/a/24025447/276626) and an [example of `didSet` from @cSquirrel](https://stackoverflow.com/a/25355948/276626) – Paul Solt Aug 31 '18 at 16:23

11 Answers11

272

Setters and Getters apply to computed properties; such properties do not have storage in the instance - the value from the getter is meant to be computed from other instance properties. In your case, there is no x to be assigned.

Explicitly: "How can I do this without explicit backing ivars". You can't - you'll need something to backup the computed property. Try this:

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

Specifically, in the 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
Jakub Truhlář
  • 20,070
  • 9
  • 74
  • 84
GoZoner
  • 67,920
  • 20
  • 95
  • 145
  • I was very confused as I skipped through the doc and missed the part that getter and setter are only for computed properties! Thanks for this answer. – dragonfly02 May 08 '22 at 03:36
  • And as I understand you can not set the backing field _x through the x computed property in the constructor since self won't be accessible if _x is not initialised? – Petru Lutenco May 30 '22 at 12:07
117

You can customize the set value using property observer. To do this use 'didSet' instead of 'set'.

class Point {

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

As for getter ...

class Point {

var doubleX: Int {
    get {
        return x / 2
    }
}
...
shim
  • 9,289
  • 12
  • 69
  • 108
cSquirrel
  • 1,402
  • 1
  • 9
  • 10
111

Setters/getters in Swift are quite different than ObjC. The property becomes a computed property which means it does not have a backing variable such as _x as it would in ObjC.

In the solution code below you can see the xTimesTwo does not store anything, but simply computes the result from x.

See Official docs on computed properties.

The functionality you want might also be Property Observers.

What you need is:

var x: Int

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

You can modify other properties within the setter/getters, which is what they are meant for.

shim
  • 9,289
  • 12
  • 69
  • 108
Jack
  • 16,677
  • 8
  • 47
  • 51
  • 1
    Yes, that's what I am reading in the documentation. I skipped over the property section and it seems there is no way around this, right? – Atomix Jun 03 '14 at 21:45
  • Yes, you will need another instance variable to "back" the first one if you really want to do this. However, setters are not meant for this purpose you should probably re-think what you are trying to accomplish with this. – Jack Jun 03 '14 at 21:46
  • 5
    you could use `didSet` which will allow you to change the value immediately after it is set. Nothing for the get though... – ObjectiveCsam Jul 09 '14 at 12:26
  • 3
    NOTE: If you want to revert the value because it was invalid input, you'd need to set `x` back to `oldValue` in the `didSet`. This change in behavior is very confusing coming from Objective-C properties, thanks! – Paul Solt Aug 31 '18 at 16:09
30

To elaborate on GoZoner's answer:

Your real issue here is that you are recursively calling your 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
        }
    }

Like the code comment suggests above, you are infinitely calling the x property's getter, which will continue to execute until you get a EXC_BAD_ACCESS code (you can see the spinner in the bottom right corner of your Xcode's playground environment).

Consider the example from the 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)
        }
    }
}

Notice how the center computed property never modifies or returns itself in the variable's declaration.

Abdullah Bakhsh
  • 658
  • 1
  • 9
  • 15
  • The rule of the thumb is **never** access the property itself from *within* the getter ie `get`. Because that would trigger another `get` which would trigger another . . . Don't even print it. Because printing also requires to 'get' the value before it can print it! – mfaani Jun 05 '18 at 19:36
19

In order to override setter and getter for swift variables use the below given code

var temX : Int? 
var x: Int?{

    set(newX){
       temX = newX
    }

    get{
        return temX
    }
}

We need to keep the value of variable in a temporary variable, since trying to access the same variable whose getter/setter is being overridden will result in infinite loops.

We can invoke the setter simply like this

x = 10

Getter will be invoked on firing below given line of code

var newVar = x
Mihriban Minaz
  • 3,043
  • 2
  • 32
  • 52
Sebin Roy
  • 834
  • 9
  • 10
16

Update: Swift 4

In the below class setter and getter is applied to variable 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
        }
   }

Creating object

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

Getter

print(triangle.perimeter) // invoking getter

Setter

triangle.perimeter = 9.9 // invoking setter
Community
  • 1
  • 1
Saranjith
  • 11,242
  • 5
  • 69
  • 122
8

You are recursively defining x with x. As if someone asks you how old are you? And you answer "I am twice my age". Which is meaningless.

You must say I am twice John's age or any other variable but yourself.

computed variables are always dependent on another variable.


The rule of the thumb is never access the property itself from within the getter ie get. Because that would trigger another get which would trigger another . . . Don't even print it. Because printing also requires to 'get' the value before it can print it!

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

let p1 = Person()

As that would give the following warning:

Attempting to access 'name' from within it's own getter.

The error looks vague like this:

enter image description here

As an alternative you might want to use didSet. With didSet you'll get a hold to the value that is was set before and just got set to. For more see this answer.

mfaani
  • 33,269
  • 19
  • 164
  • 293
7

Try using this:

var x:Int!

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

This is basically Jack Wu's answer, but the difference is that in Jack Wu's answer his x variable is var x: Int, in mine, my x variable is like this: var x: Int!, so all I did was make it an optional type.

Tapas Pal
  • 7,073
  • 8
  • 39
  • 86
Epic Defeater
  • 2,107
  • 17
  • 20
1

Setters and getters in Swift apply to computed properties/variables. These properties/variables are not actually stored in memory, but rather computed based on the value of stored properties/variables.

See Apple's Swift documentation on the subject: Swift Variable Declarations.

jefe2000
  • 406
  • 6
  • 13
1

Here is a theoretical answer. That can be found here

A { get set } property cannot be a constant stored property. It should be a computed property and both get and set should be implemented.

Talha Ahmad Khan
  • 3,416
  • 5
  • 23
  • 38
0

Update for Swift 5.1

As of Swift 5.1 you can now get your variable without using get keyword. For example:

var helloWorld: String {
"Hello World"
}
atalayasa
  • 3,310
  • 25
  • 42