8

I have a simple class below

import Foundation

public class UsefulClass: NSObject{
    var test:NSNumber{
        get{return self.test}
        set{
            println(newValue)
            self.test = newValue
        }
    }
    override init() {
        super.init()
        self.test = 5;
    }
}

and I'm initializing it here

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        var testClass = UsefulClass()
    }
}

But it results in xcode printing out 200 5s and then crashing due to EXC_BAD_ACCESS code = 2. Why does this happen?

user3211122
  • 143
  • 1
  • 4

3 Answers3

14

@vadian has provided a solution in his answer, which should fix your problem. Let me just explain what's happening.

You have created a computed property, i.e. a property which is not backed by a variable, instead both the getter and the setter do some processing, usually on another stored property, in order to respectively return a value and set a new value.

This is your computed property:

var test: NSNumber {
    get { return self.test }
    set {
        println(newValue)
        self.test = newValue
    }
}

Look at the getter implementation:

return self.test

What does it do? It reads the test property of the current instance, and returns it. Which is the test property? It's this one:

var test: NSNumber {
    get { return self.test }
    set {
        println(newValue)
        self.test = newValue
    }
}

Yes, it's the same property. What your getter does is to recursively and indefinitely calling itself, until a crash happen at runtime.

The same rule applies to the setter:

self.test = newValue 

it keeps invoking itself, until the app crashes.

Antonio
  • 71,651
  • 11
  • 148
  • 165
  • 1
    I see, so there is no way to override the setter in Swift to do something like set{ self.test = newValue - 7} without another stored property? – user3211122 Jul 03 '15 at 17:38
  • 1
    Oh nvm, http://stackoverflow.com/questions/24006234/what-is-the-purpose-of-willset-and-didset-in-swift was really helpful. So set/get are only methods and didSet/willSet are tied to ivars. – user3211122 Jul 03 '15 at 17:51
7

Swift variables are synthesized properties by default. In the most cases this is sufficient (it's recommended to prefer Swift types)

var test: Int

override init() {
    super.init()
    test = 5
}

If you need to do something after a variable is set, use

var test: Int {
    didSet{
        println("\(oldValue) - \(newValue)")
    }
}

your code sets the variable permanently by calling the setter which calls the setter which …

vadian
  • 274,689
  • 30
  • 353
  • 361
0

It's an infinite loop; your setter is recursively calling itself.

var test: NSNumber {
    set {
        test = newValue
    }
}

This compiles fine, and an Objective-C programmer might expect no loop due to instead setting a "backing ivar" such as _test rather than re-calling the setter method.

But property-backing instance variable _ivars do not exist in Swift for computed properties unless you create them yourself.

pkamb
  • 33,281
  • 23
  • 160
  • 191