Swift Override Instance Variables

Swift override instance variables

As you say, you cannot simply redefine a constant in a subclass (it is a constant, after all). The error you get is "Cannot override with a stored property". It does appear to be possible to override a var, however, when I change the let someVariable to var someVariable I get "ambiguous use of 'someVariable'" when I access it in the subclass (note - same thing happens whether I use override or not).

The simplest solution is to use a getter. This is really a function, so you can happily override it, the backing variable will be managed for you, and if you don't supply a setter ... it will be constant for each class:

class BaseView: UIView {
var someVariable: Int { get { return 1 } }
// do some work with someVariable
}

class ExtendedView: BaseView {
override var someVariable: Int { get { return 2 } }
}

let a = BaseView()
a.someVariable // 1
let b = ExtendedView()
b.someVariable // 2

As commentator @user3633673 points out, if you only have a getter (and not a setter), you can drop the get, but I left it in for clarity of the principle. Here's the same without it...

class BaseView: UIView {
var someVariable: Int { return 1 }
// do some work with someVariable
}

class ExtendedView: BaseView {
override var someVariable: Int { return 2 }
}

let a = BaseView()
a.someVariable // 1
let b = ExtendedView()
b.someVariable // 2

... and, of course, in Swift 5, you can drop the return:

class BaseView: UIView {
var someVariable: Int { 1 }
}

class ExtendedView: BaseView {
override var someVariable: Int { 2 }
}

Swift Unable to Override Variables in Subclass

EDIT

@Knight0fDragon was right, a better approach is to use super.texture

class classTwo: classOne {
override var texture:SKTexture? {
get {
return super.texture
}
set {
super.texture = newValue
}
}
}

In order to override the property you need to initialise it. In other words, giving it an initial value. If you don't want, here is a workaround:

class classTwo: classOne {    
var _texture:SKTexture
override public var texture: SKTexture? {
get {
return _texture
}
set {
_texture = newValue!
}
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// Some other functions
}

Overriding default value of instance var

Define an init() method as:

init () {
super.init()
x = "y"
}

You'll want any other initializers in Obj1 to invoke this as self.init(). The Apple documentation has a long discussion on designated initializers and inheritance vis-a-vis initializers.

Overriding a stored property in Swift


Why am I not allowed to just give it another value?

You are definitely allowed to give an inherited property a different value. You can do it if you initialize the property in a constructor that takes that initial value, and pass a different value from the derived class:

class Jedi {
// I made lightSaberColor read-only; you can make it writable if you prefer.
let lightSaberColor : String
init(_ lsc : String = "Blue") {
lightSaberColor = lsc;
}
}

class Sith : Jedi {
init() {
super.init("Red")
}
}

let j1 = Jedi()
let j2 = Sith()

print(j1.lightSaberColor)
print(j2.lightSaberColor)

Overriding a property is not the same as giving it a new value - it is more like giving a class a different property. In fact, that is what happens when you override a computed property: the code that computes the property in the base class is replaced by code that computes the override for that property in the derived class.

[Is it] possible to override the actual stored property, i.e. lightSaberColor that has some other behavior?

Apart from observers, stored properties do not have behavior, so there is really nothing there to override. Giving the property a different value is possible through the mechanism described above. This does exactly what the example in the question is trying to achieve, with a different syntax.

Protected variable in an objective c super class is inaccessible in a swift subclass

As Alexander Explained in his answer, this is not "straightforwardly" achievable.

Since I'm not willing to alter the objective C super class, I decided to depend on KVC to achieve what I need.

 (value(forKey: "thisStringIsInaccessibleInSwiftSubclasses") as! NSString)//you now have access to the @protected instance variable 

I'll then create a main swift class and do all the KVC work in it.

Updating an instance variable for an object from another class in Swift

This is the first viewController:

class ViewController: UIViewController, ViewControllerSecondDelegate {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Next" {
let vc = segue.destination as? ViewControllerSecond
vc?.delegate = self
}
}
func secondDelegate(_ value: Bool) {
print("delegate") //needUpdating = value
}
}

and this is the secondViewController:

protocol ViewControllerSecondDelegate {
func secondDelegate(_ value: Bool)
}

class ViewControllerSecond: UIViewController {
weak var delegate: ViewControllerSecondDelegate?

override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
delegate?.secondDelegate(true) //call this wherever you need it.
}
}

Initialize instance variables inside instance function

This occurs because

var someVar: Footer!

does not define a variable of type Footer but a variable of type Optional<Footer> that is implicitly unwrapped. The code:

var someFooter: Footer! 

bind(footer: &someFooter)

is logically equivalent to

var someFooter: Footer? 

guard let tempFooter = someFooter? else { fatalError() }
bind(footer: &tempFooter)

As you can see, tempFooter is a let, so it can't be passed as an inout variable and even if it could, the result would be thrown away.

You can fix this in one of three ways:

  • make the parameter to bind an optional, e.g. func bind(footer: inout Footer?) or use Martin's syntax to make it implicitly optional.

  • Force the unwrap yourself:

    var unwrapped: Footer = someFooter
    bind(footer: unwrapped)
    someFooter = unwrapped
  • redesign the API. It seems the first thing you do in the bind function is overwrite the old footer with a newly initialised footer. So don't use an inout parameter, return the value you want i.e.

    func bind() -> Footer
    {
    var theFooter = Footer(...) { ... }
    // do stuff

    return theFooter
    }

    someFooter = bind()

I think the last option is the best in this case.

If I subclass a class, can I specify a certain subclass an instance variable should be?

How about using generic? For simplicity, I removed NSObject

class MyObject {

}

class MyObjectA: MyObject {

}

class MyObjectB: MyObject {

}

class BaseClass<T> where T : MyObject {
var myObject: T?
}

class SubClassA: BaseClass<MyObjectA> {
}

class SubClassB: BaseClass<MyObjectB> {
}


Related Topics



Leave a reply



Submit