swift setter causing exc_bad_access - swift

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?

#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.

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 …

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.

Related

Swift variable observers not called before super.init called

Okay so I was reading up on how willSet/didSet are used in swift and I came across a note on apples swift docs that just doesn't make any sense to me and I hope someone can explain. Here's the note:
The willSet and didSet observers of superclass properties are called
when a property is set in a subclass initializer, after the superclass
initializer has been called. They are not called while a class is
setting its own properties, before the superclass initializer has been
called.
From: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html
What confuses me is that they point out that the observers on superclass A properties in a subclass B aren't called before the super.init call by B to A.
class A {
var p: Bool
init() {
p = false
}
}
class B: A {
override var p: Bool {
didSet {
print("didSet p")
}
}
override init() {
p = true // Compiler error
super.init()
}
}
However the property is never even accessible in that time from either A nor B, so who's gonna call the observers anyway? Attempting to read/write the property will even result in a compiler error so it's never even possible to do it by mistake in Swift. Am I missing something or is this just a misleading note that points out the wrong thing?
They are talking about following scenario:
class A {
var p: Bool {
didSet {
print(">>> didSet p to \(p)")
}
}
init() {
p = false // here didSet won't be called
}
}
class B: A {
override init() {
// here you could set B's properties, but not those inherited, only after super.init()
super.init()
p = true // here didSet will be called
}
}
B()
It will print following:
>>> didSet p to true
While to you it might seems natural, the documentation has to explicitly document this behavior.

Variable with getter/setter cannot have initial value, on overridden stored property

When creating a stored property with Observing Accessors, I can specify a default value. However, when overriding a stored property and its Accessors I cannot specify a default value.
Variable with getter/setter cannot have initial value.
Which seems very strange, as this is NOT a computed property with a getter/setter, but a set of Observing Accessors on a stored property!
class FirstViewController: UIViewController {
internal var test: Float = 32.0 {
willSet {
}
didSet {
}
}
The first view controller compiles fine, with a stored property initialized to 32.0
class SecondViewController: FirstViewController {
override var test: Float = 64.0 {
willSet {
}
didSet {
}
}
The second view controller does not compile, as the 'computed property' is being given an initial value
In swift you are able to override properties only with computed properties (which are not able to have default values) with same type. In your case, if you wish override test property in SecondViewController you need write something like this:
override var test: Float {
get {
return super.test
}
set {
super.test = newValue
}
}
And there is no way to override didSet/willSet observers directly; you may do this by write other methods invoked in observers and just override them:
FirstViewController:
internal var test: Float = 32.0 {
willSet {
test_WillSet(newValue)
}
didSet {
test_DidSet(oldValue)
}
}
func test_WillSet(newValue: Float) {}
func test_DidSet(oldValue: Float) {}
SecondViewController:
override func test_WillSet(newValue: Float) {
super.test_WillSet(newValue)
}
override func test_DidSet(oldValue: Float) {
super.test_DidSet(oldValue)
}
I know that this has been asked a long time ago but I came up with a slightly different solution and it works exactly as you wanted. You have a property in the first ViewController then in the inherited one you override it and have observers set on it in the form of didSet.
So in the FirstViewController you have a property like in the example below:
var myNumber: Double = 20.00
Then in the SecondViewController which inherits from FirstViewController you override it as follows:
override var myNumber: Double {
didSet {
//Here you can update UI or whatever you want to do once the property changes
//Print its value
print("Value of myNumber is : \(myNumber)")
}
I hope this will help someone with the above issue as this is a nice and easy way to solve the problem mentioned above.

Swift initialization stored property outside init method of class issue

I have an swift class
class ApplicationManager {
var fanMode: FanMode
init()
{
self.applyDefaultSettings()
}
func applyDefaultSettings()
{
if let unwrappedFanMode = userDefaults.valueForKey(Consts.kStoredFanMode) as? FanMode {
self.fanMode = unwrappedFanMode
}
}
}
The code above throws this issue:
Use of 'self' in method call 'applyDefaultSettings' before all stored properties are initialized
What should I do here? So as message say I need to initialize all stored properties before I call any other method of class. So it means in init method I should initialize at least fanMode property. But I want to have method that apply kind of default settings for my properties to provide simple readability and clean code architecture. But maybe it's ok to use initializer of class to init all needed fields.
You also can do it by using this code:
var fanMode: FanMode = {
if let unwrappedFanMode = userDefaults.valueForKey(Consts.kStoredFanMode) as? FanMode {
return unwrappedFanMode
} else {
return FanMode()//some default implementation
}
}()
It is readable as You want.
As per Apple documentation, Swift does not allow you to left uninitialised variables or constants. If you want to set some default settings then assign your variables with initial values that will act as your default settings and later you can change them.
All instance properties must be initialized in the init method. You can either move the initialization to the init (defaultMode would be your default value if userDefaults is nil):
init() {
fanMode = (userDefaults?.valueForKey(Consts.kStoredFanMode) as? FanMode) ?? defaultMode
}
Set a default value for that property, for example:
var fanMode: FanMode = defaultMode
Or you can make your fanMode nullable:
var fanMode: FanMode? = nil
You can use an implicity unwrapped optional. Just add a ! to the variable declaration.
class ApplicationManager {
var fanMode: FanMode! //Implicitly unwrapped optional.
init()
{
self.applyDefaultSettings()
}
func applyDefaultSettings()
{
if let unwrappedFanMode = userDefaults.valueForKey(Consts.kStoredFanMode) as? FanMode {
self.fanMode = unwrappedFanMode
}
}
}
Basically it tricks xCode into telling it "Hey this variable is initialized and value will never be nil". But you want to be careful using these as if it does turn out to be nil your program will crash. But in your case it should be fine since you initialize it in the init method so it will never be nil before using it.

Swift 2 version of returning optional variables?

I'm trying to implement getters/setters for properties. In the computed property, I'm returning the _locationManager, but also initializing the first time only:
var _locationManager: CLLocationManager? { get set }
var locationManager: CLLocationManager {
if let manager = _locationManager {
return manager
}
_locationManager = CLLocationManager()
return _locationManager!
}
Is there a more Swift-like version of the above statement? For example, in C# I can do something like this:
var locationManager: CLLocationManager {
return _locationManager ?? (_locationManager = CLLocationManager())
}
This is saying return _locationManager, or initialize plus return it. Does Swift have some kind of shorthand or another way for this scenario?
Update:
Below is another version. I'm trying to stick with a computed property so it can be used in protocol extensions and/or able to wire up a delegate in the process etc:
var locationManager: CLLocationManager {
return _locationManager ?? {
self._locationManager = CLLocationManager()
return self._locationManager!
}()
}
you could use a lazy property for this:
lazy var locationManager: CLLocationManager = CLLocationManager()
a lazy property gets initialized only when it is first used.
If you're really desperate to, you can do something like you're asking, but it's not terribly pretty. However, in the example you quoted, you should use lazy instantiation as Palle answered.
class Test {
}
var _test: Test?
var test: Test {
get {
return _test ?? { _test = Test() ; return _test! }()
}
}
Declaring a closure like this gets you the 'cute' one line getter, but I would leave it as you've got it because it's clearer.

Swift - Custom setter on property

I am converting a project in to Swift code and have come across an issue in a setter. My Objective-C code looked like this:
- (void)setDocument:(MyDocument *)document
{
if (![_document isEqual:document]) {
_document = document;
[self useDocument];
}
}
and allowed my View Controller to run this each time the document was set (typically in the prepareForSegue: method of the presenting View Controller).
I have found the property observers willSet and didSet but they only work when the property is being updated, not when it’s initialised and updated.
Any ideas? Thanks
UPDATE
after trying get{} and set{} I get the EXC_BAD_ACCESS error
var document: UIDocument? {
get {
return self.document!
}
set {
self.document = newValue
useDocument()
}
}
You can't use set like that because when you call self.document = newValue you're just calling the setter again; you've created an infinite loop.
What you have to do instead is create a separate property to actually store the value in:
private var _document: UIDocument? = nil
var document: UIDocument? {
get {
return self._document
}
set {
self._document = newValue
useDocument()
}
}
Here's a Swift 3 version
var document : UIDocument? {
didSet {
useDocument()
}
}