Swift let is mutable in classes why? - swift

Hello everyone I am trying to figure out why the swift code below allows me to assign a new value to the wee string in my class. I thought let was immutable but it works here. Can someone please explain this. Thanks.
import Foundation
class MyClass {
let wee:String
init?(inInt:Int) {
let j:String = "what"
//j = "I shouldn't be able to do this wiht let" // error rightly so
//println(j)
self.wee = "wow"
if inInt != 2 {
return nil
}
self.wee = "hey"
self.wee = "blas" // I shouldn't be able to do this
}
}
if let myClass:MyClass = MyClass(inInt: 2) {
myClass.wee // prints blas
}

The "Modifying Constant Properties During Initialization" heading under the Initialization section of The Swift Programming Language says:
You can modify the value of a constant property at any point during
initialization, as long as it is set to a definite value by the time
initialization finishes.
Reading between the lines, and considering your example, it sounds very much like restrictions on setting the value of a constant don't apply to initialization. Further evidence supporting that idea appears earlier in the same section:
When you assign a default value to a stored property, or set its
initial value within an initializer, the value of that property is set
directly, without calling any property observers.
It's not unlikely that the constancy of a stored property is enforced by the accessors for that property. If those accessors aren't used during initialization, then it makes sense that you can modify even a constant property as many times as you like during initialization.
The fact that you can't modify j in your example after first setting it is due to the fact that j is a local constant, not a property. There probably aren't any accessors for j at all -- instead the compiler probably enforces access rules for local constants/variables.

Because you are assigning it in initialiser, at a point when object of that class is being created. I just assume, it take any last given value and then creates a constant.
If you would try the same in different function, it would not work. You would get an error.

It is not possible in swift 4.
Apple docs says:
You can assign a value to a constant property at any point during initialization, as long as it is set to a definite value by the time initialization finishes. Once a constant property is assigned a value, it can't be further modified.

Related

How to write swift properties calculated in later stage of Object's lifecycle

I have a swift UIViewController in which there is an array of data type ( e.g. countries/person/ bank details) fetched from network service asynchronously in viewDidLoad. This array remains constant once fetched. Hence I want to enforce it using let keyword.
I don't have any value when UIViewController is initialized. Hence it gives me compile time error for not initializing it.
If I declare it as optional with '?' , I have to use if-let/ guard let or optional chaining to use it. I don't want to clutter the code with unwrapping.
What are my options to declare a variable as constant but initialized later in execution without making it an optional variable?
"Constant once fetched" is not constant. There is some period of time when it's not set, and some point later when it is. The fact that it's fetched from the network means that it may fail, so your code has to deal with that (i.e. it may never be set). The right tool to use here is an Optional var.
Since there must be some view state that handles "no data yet" and a different view state that handles "data received," you can improve your design by breaking those into two view controllers, and having your container view controller switch between them when the data becomes available. In that case, you can pass the available data to the "data received" view controller in init, and so it can be let.
What are my options to declare a variable as constant but initialized later
There are no such options. If you do not have the real value of a property at instantiation time, it must be declared with var so that you can set it when you do have the real value.
And use of an Optional is a common pattern to help your code distinguish between before you have the real value (nil) and after (not nil).
I don't want to clutter the code with unwrapping.
Then declare the property as an implicitly unwrapped Optional! This use case is exactly what it is good for.
(It would be nice to be able to “lock” the property somehow after assigning its final value, but that is not a Swift language feature. I have often wished it were. lazy has the same issue.)

Swift optional binding constant names

I am just transitioning from Objective-C to Swift and have been constantly writing the following code for optional binding,
if let tempX = X {
}
My question is I have to do it so often that I need to find a new name for constant every time. What's the way to avoid having a new name tempX for every optional X in the code? Wouldn't something like work?
if let X = X {
}
Yes, you can reuse the same identifier in such bindings. But keep in mind that the redefined, non-optionalX will only be visible inside the if scope.
But if you use a guard statement, the new X may shadow the previous variable (i.e., if previous variable was defined in another scope; otherwise will trigger a compiler error). Some would say this could hurt your code readability.
For further information, please see this related question.
The new constant is only defined within the scope of your if let, so as soon as the scope is over, your tempX is gone. That also means that you can reuse names within the if let scope.
if let X = X {
}

Lazy var with getter loads 1 one or everytime? [duplicate]

Are lazy vars in Swift computed more than once? I was under the impression that they replaced the:
if (instanceVariable) {
return instanceVariable;
}
// set up variable that has not been initialized
Paradigm from Objective-C (lazy instantiation).
Is that what they do? Basically only called once the first time the app asks for the variable, then just returns what was calculated?
Or does it get called each time like a normal computed property?
The reason I ask is because I basically want a computed property in Swift that can access other instance variables. Say I have a variable called "fullName" and it just concatenates firstName and lastName. How would I do that in Swift? It seems like lazy vars are the only way to go, as in normal computed vars (non-lazy) I can't access other instance variables.
So basically:
Do lazy vars in Swift get called more than once? If so, how do I create a computed variable that can access instance variables? If not, if I only want a variable to be computed once for performance reasons, how do I do this?
lazy vars are only calculated once, the first time you use them. After that, they're just like a normal variable.
This is easy to test in a playground:
class LazyExample {
var firstName = "John"
var lastName = "Smith"
lazy var lazyFullName : String = {
[unowned self] in
return "\(self.firstName) \(self.lastName)"
}()
}
let lazyInstance = LazyExample()
println(lazyInstance.lazyFullName)
// John Smith
lazyInstance.firstName = "Jane"
println(lazyInstance.lazyFullName)
// John Smith
lazyInstance.lazyFullName = "???"
println(lazyInstance.lazyFullName)
// ???
If you'll want to recalculate it later, use a computed property (with a backing variable, if it's expensive) - just like you did in Objective-C.
No, lazy properties are initialized only once. If you set a new value, or reset to nil (for optional properties), the lazy initializer is not invoked again.
I think what you need is a computed property - it's not backed by a stored property, so it is not involved in the initialization, and as such you can refer other instance properties.
Why do you say that "normal computed vars (non-lazy) I can't access other instance variables"?
All the other answers are correct, I would just like to add that Apple warns about lazy variables and concurrency:
If a property marked with the lazy modifier is accessed by multiple
threads simultaneously and the property has not yet been initialized,
there is no guarantee that the property will be initialized only once.
Answers stating that a lazy var can only be computed once are not true. From the documentation at https://docs.swift.org/swift-book/LanguageGuide/Properties.html, the following is stated:
If a property marked with the lazy modifier is accessed by multiple
threads simultaneously and the property has not yet been initialized,
there is no guarantee that the property will be initialized only once.
Also, please watch this talk: https://developer.apple.com/videos/play/wwdc2016/720/. At around 17:00, the following screen appears:
That talk gives you more insight about multithreading, I recommend you to watch it!

Optional properties in swift

If I have property like:
var animatedImagesView:JSAnimatedImagesView?
And eventually it gets initialized at the proper time, do I need to just keep using ! to unwrap it ad nauseum when I want to do something to it? For instance:
self.animatedImagesView!.reloadData()
Usually I unwrap optionals like:
if let dailySleepTime:AnyObject = uw_JSON["daily_sleep_time"] {
self.settings.dailySleepTime = dailySleepTime as String
} else {
log.warning("\n Error finding attr in \(request)\n")
}
but I can't just go around casting my properties to constants in the same way right? I'm not complaining, I'm just wondering if I'm using the exclamation point correctly.
One option is to define animatedImageView as an implicitly unwrapped optional to begin with:
var animatedImagesView: JSAnimatedImagesView!
This is common when dealing with Cocoa .nib objects in Interface Builder, because the View can't be initialized until it is unarchived from the .nib, but you know that it will always be initialized before you use it.
I hate using the ! in general, because it is a runtime error just waiting to happen, but IB objects are one of the few places where its use seems both legitimate and necessary. Otherwise, your two other options are the ones that you have already found - unwrapping it every time using if let... (which is safest, but a pain in the a**), or using the ! every time that you use it, which isn't any safer than just declaring it using ! to begin with.
If you initialize it at the correct time (for example in any of the lifecycle methods, or in the constructor for that matter) you could just use ! instead of ? for the property declaration. I myself usually only use ? for stuff which I dont necessarily know will have a value at all times during my lifecycle.
If the latter is the case for you as well, you need to continue using ? since you can't guarantee you have a value stored. And you should also use the if let myVar = myVar { .. } syntax if you do multiple calls to the variable.
Instead, declare it as follows:
var animatedImagesView:JSAnimatedImagesView!
That implicitly unwrapped optional will obviate the need to constantly "bang" dereference it, but be sure it is never nil at any time accessed.
It's a great way to specify IBOutlets you know will always be initialized from the nib or storyboard before you use them.

Are lazy vars in Swift computed more than once?

Are lazy vars in Swift computed more than once? I was under the impression that they replaced the:
if (instanceVariable) {
return instanceVariable;
}
// set up variable that has not been initialized
Paradigm from Objective-C (lazy instantiation).
Is that what they do? Basically only called once the first time the app asks for the variable, then just returns what was calculated?
Or does it get called each time like a normal computed property?
The reason I ask is because I basically want a computed property in Swift that can access other instance variables. Say I have a variable called "fullName" and it just concatenates firstName and lastName. How would I do that in Swift? It seems like lazy vars are the only way to go, as in normal computed vars (non-lazy) I can't access other instance variables.
So basically:
Do lazy vars in Swift get called more than once? If so, how do I create a computed variable that can access instance variables? If not, if I only want a variable to be computed once for performance reasons, how do I do this?
lazy vars are only calculated once, the first time you use them. After that, they're just like a normal variable.
This is easy to test in a playground:
class LazyExample {
var firstName = "John"
var lastName = "Smith"
lazy var lazyFullName : String = {
[unowned self] in
return "\(self.firstName) \(self.lastName)"
}()
}
let lazyInstance = LazyExample()
println(lazyInstance.lazyFullName)
// John Smith
lazyInstance.firstName = "Jane"
println(lazyInstance.lazyFullName)
// John Smith
lazyInstance.lazyFullName = "???"
println(lazyInstance.lazyFullName)
// ???
If you'll want to recalculate it later, use a computed property (with a backing variable, if it's expensive) - just like you did in Objective-C.
No, lazy properties are initialized only once. If you set a new value, or reset to nil (for optional properties), the lazy initializer is not invoked again.
I think what you need is a computed property - it's not backed by a stored property, so it is not involved in the initialization, and as such you can refer other instance properties.
Why do you say that "normal computed vars (non-lazy) I can't access other instance variables"?
All the other answers are correct, I would just like to add that Apple warns about lazy variables and concurrency:
If a property marked with the lazy modifier is accessed by multiple
threads simultaneously and the property has not yet been initialized,
there is no guarantee that the property will be initialized only once.
Answers stating that a lazy var can only be computed once are not true. From the documentation at https://docs.swift.org/swift-book/LanguageGuide/Properties.html, the following is stated:
If a property marked with the lazy modifier is accessed by multiple
threads simultaneously and the property has not yet been initialized,
there is no guarantee that the property will be initialized only once.
Also, please watch this talk: https://developer.apple.com/videos/play/wwdc2016/720/. At around 17:00, the following screen appears:
That talk gives you more insight about multithreading, I recommend you to watch it!