Are Swift constants lazy by default? - swift

Something I still not quite understand about Swift ... let's say I want a property that instantiates a class in a base class used for several sub classes, e.g. ...
let horse = Horse();
Is horse instantiated right after app/class initialisation or when the property is accessed for the first time?
On the other hand using lazy var guarantees that the property is only instantiated the first time it is accessed ...
lazy var horse = Horse()
But then horse is not a constant. So in this case if I access horse multiple times I would get multiple instances of horse created, right?
What if I wanted both, a lazy property that is also a constant?

Not exactly. Say you have a class Farm and inside Farm there is horse property.
class Farm {
let horse = Horse()
}
In this case horse property is initialized when class instance initialized. If you make it lazy you have to make it mutable too.
class Farm {
lazy var horse = Horse()
}
In this case, horse property is initialized when it is accessed the first time. And later when it is accessed again it returns the same instance again instead of reinitializing it. But since it is a mutable property you can assign a new instance of Horse to it. After you assign it new value it will return this new value whenever it is accessed.
EDIT: If let horse = Horse() is defined in global space then it is lazily created at first access.

Well, a bit late, but I think you can create a lazy property as constant using private(set) . Consider the example below :
import Foundation
struct GPS: CustomStringConvertible {
let name: String
init(name: String) {
self.name = name
print("GPS Initialised")
}
var description: String {
return name
}
}
struct Car {
private(set) lazy var gps = GPS(name: "One")
init() {
print("Car Initialised")
}
}
var someCar = Car()
print(someCar.gps) // Comment/Uncomment this to see lazy in action
//someCar.gps = GPS("Two") // This will create a compilation error
//
However, if you remove the private(set), it(gps variable inside Car) will become a variable again which can be mutated.
You can uncomment the last line after removing the private(set) to verify the same

Related

Can you pick and choose what gets inherited in a sub-class in Swift?

I have a base class (Spaceship) that has some variables and an initializer.
I also have a subclass (UFO) that only needs one of the variables of the superclass.
When I try and create a basic UFO object, I get: "missing argument for parameter 'weapon' in call var c = UFO()"
I realize that error message is asking for a weapon parameter, but how do I (and is it even possible?) to create a basic UFO object that doesn't need a weapon parameter?
Have spent hours tinkering with "override", "init" and "super" commands. I've read a lot of the articles here on SO and other sites, but have not seen any examples similar to my situation. Again, I'm assuming that what I want to do is possible to begin with.
class Spaceship {
var isUFO = false
var weapon:String
init(weapon:String) {
self.weapon = weapon
}
func printSomething() {
print("Test")
}
}
class UFO: Spaceship {
}
var a = Spaceship(weapon:"Laser")
a.printSomething() //OUTPUT: Test
var b = UFO(weapon:"")
//This runs, but I don't want to have to fill in parameters
b.printSomething() //OUTPUT: Test
var c = UFO() //Ultimately this is what I want to do
c.printSomething()
//OUTPUT: missing argument for parameter 'weapon' in call var c = UFO()
What you can do is mark weapon as optional String. Also, in init(weapon:) use default value of weapon as nil, i.e
class Spaceship {
var isUFO = false
var weapon: String? //here...
init(weapon: String? = nil) { //here...
self.weapon = weapon
}
func printSomething() {
print("Test")
}
}
Now, you can simply create the object of class UFO both with and without weapon, i.e.
var a = Spaceship(weapon:"Laser")
var b = UFO(weapon:"Another Weapon")
var c = UFO()
First, when using inheritence, you'll have to think about an "is a" relationship: An UFO is a spaceship. So when each spaceship has a weapon, and an UFO is a spaceship, then every UFO also has to have a weapon.
What you could do is make your weapon an optional, but I think this is somehow a mis-usage. Also, re-think your isUFO property:
A spaceship should not make assumptions about special subclasses
You could use the is operator to check the dynamic type of a Spacehip
If you don't want this inheritence behaviour, you might better think about something else, like protocols, where you only specify certain behaviour but no memory layout.

Why do Singleton's capture an instance of themselves?

I'm trying to understand what happens at a lower-level when we create the first instance of a Singleton object within the Singleton's own class declaration.
As I understand it, the static keyword allows the marked property or method to be shared across all instances of a class, which I'm sure has a role here that I'm not seeing fully.
Additionally, how does this work when we consider the instance's creation during compilation/runtime?
Here's an example of a Singleton class declaration:
class Person {
static let details = Person()
var name = "Alan Turing"
let age = "42"
}
I understand that the class and its properties will only be created once and that any reference to the class object will point back to that same point in memory. My confusion is specifically about why we create the Singleton's first instance within itself.
Basically static variables are class variables that are always accessible via the class itself.
I suggest reading a more in depth explanation of static here
By having a static variable inside the class that contains an object of the class itself you ensure that at runtime there already is an object of that class (or at least at the time of accessing Person.instance for the first time).
For a true singleton inside Swift it is mandatory to make the init of that class private though, like so:
public class Person {
static let instance = Person()
var name: String
let age: Int
private init() {
self.name = "Alan Turing"
self.age = 42
}
}
That way you ensure that there really is only one object present at any give time (hence the name singleton).

Creating a global variable in swift

I am trying to create a global variable from my view controller (inside the class but outside the functions) as follows:
var modelData: CustomTabBarController.model // THE ERROR IS HERE
This is how that class is defined:
CustomTabBarController.swift:
import UIKit
// This class holds the data for my model.
class ModelData {
var name = "Fred"
var age = 50
}
class CustomTabBarController: UITabBarController {
// Instantiate the one copy of the model data that will be accessed
// by all of the tabs.
var model = ModelData()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
However I am getting the following error:
"'model' is not a member type of "CustomTabBarController"
How do I declare it so that I can access model? Thanks.
Update #1
Sorry I forgot to mention this:
I need the model data to be the SAME in every tab of the tabbar. For example if I change the age to 51 in the first tab, the second tabbar should retrieve 51. Which would be the correct method above to use it this way?
Update #2
I am able to create the variable inside a function with dean's suggestion:
func setupModelData()
{
var modelData = (self.tabBarController as! CustomTabBarController).model
}
However this does not work, since I need to access the modelData from other functions. When I attempt to move this line outside of the function as follows:
import UIKit
class FirstViewController: UIViewController, UITableViewDelegate, UITableViewDataSource
{
var modelData = (self.tabBarController as! CustomTabBarController).model
...
I receive the error:
Value of type '(NSObject) -> () -> FirstViewController' has no member 'tabBarController'
I ended up following holex's suggestion of creating a shared class (singleton):
import UIKit
class ModelData: NSObject {
static let shared: ModelData = ModelData()
var name = "Fred"
var age = 50
}
Writing in first view: Set age to 51:
ModelData.shared.age = 51
Reading in second view: Get age of 51
let age = ModelData.shared.age
I'm not sure whether you truly want a global variable (i.e. a single instance of ModelData shared between all your view controllers) or an instance variable which is public, so I'll try to answer both :)
1) global model
This line attempts to get the model property from the CustomerTabBarController class - i.e. if you made multiple tab bar controllers they would all use the same model.
var modelData: CustomTabBarController.model
If this is what you want, then you need to change this line to include the static keyword.
static var model = ModelData()
However, this almost certainly isn't what you're after.
2) shared instance variable
This means that the model is part of each instance of CustomTabBarController. Here, you would need to change the line which is throwing the error to be something like this:
var modelData: myCustomTabBarController.model
Without knowing more about your architecture, I can't help you get hold of your tab bar controller instance, but something like this might work (inside other view controllers):
var modelData = (self.tabBarController as! CustomTabBarController).model
model is an instance variable.
Either create an instance of CustomTabBarController
var modelData = CustomTabBarController().model
Or declare model as class variable
static var model = ModelData()
...
var modelData = CustomTabBarController.model
However to use ModelData as a single global variable with the two members, use a struct rather than a class and declare the members as static.
struct ModelData {
static var name = "Fred"
static var age = 50
}
You can access the name from everywhere for example
let name = ModelData.name
and there is no need to create an extra variable in another class.
An – instance based –  alternative is a singleton
struct ModelData {
static let shared = ModelData()
var name = "Fred"
var age = 50
}
and use it
let name = ModelData.shared.name

ReactiveSwift mutable property with read only public access

I have a class with enum property state. This property's value (by value I mean ReactiveSwift.Property's value) needs to be accessed and observed by other classes, but value change should be private. Currently it is implemented in a such way:
enum State {
case stopped, running, paused
}
var state: Property<State> {
return Property(mutableState)
}
fileprivate let mutableState = MutableProperty<State>(.stopped)
This pattern allows me to modify mutableState property within class file. At the same time outside the class state is available only for reading and observing.
The question is whether there is a way to implement a similar thing using single property? Also probably someone can suggest a better pattern for the same solution?
I can't think of any way to do this with a single property. The only adjustment I would make to your code is to make state a stored property rather than a computed property that gets newly created each time it is accessed:
class Foo {
let state: Property<State>
fileprivate let mutableState = MutableProperty<State>(.stopped)
init() {
state = Property(mutableState)
}
}
Depending where you want to mutate the state, you can try doing either of:
private(set) var state: Property<State>
or if you modify it from an extension but still the same file
fileprivate(set) var state: Property<State>

Class or struct for hierarchy model?

I understand difference between class and struct in Swift. Now I'm wondering what to use for hierarchy model.
To define a class is pretty simple (setting connections on properties set is now irrelevant).
class XYClass {
var title: String
var subinstances: [XYClass]
weak var superinstance: XYClass?
}
But it looks like pretty fine model for struct. Especially if I need to instantiate a lots of these and frequently. But I'm wondering if I can somehow safely point to superinstance or I need to store whole object graph to every instance on every change... Should I use class or struct and if struct, how to define it?
You are making a linked list. If you were to try to form a linked list of structs of a single type, memory management would not be feasible, and the compiler would stop you dead in your tracks. This won't compile:
struct XYClass {
var title: String
var subinstances: [XYClass]
var superinstance: XYClass?
}
The compiler has spotted the problem. You cannot refer to an instance of a struct as a property of that struct. (The compiler calls this a "recursive value type".)
Thus, for your situation, you must use a class, because only then can you get a weak reference and avoid a retain cycle. Only a reference to a class can be weak (and only if the reference is typed as an Optional).
This will compile, and will give your linked list coherent memory management:
class XYClass {
var title: String = ""
var subinstances: [XYClass] = []
weak var superinstance: XYClass?
}