Are lazy vars in Swift computed more than once? - swift

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!

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

Array pass by value by default & thread-safety

Say I have a class which has an Array of object Photo:
class PhotoManager {
fileprivate var _photos: [Photo] = []
var photos: [Photo] {
return _photos
}
}
I read one article which says the following:
By default in Swift class instances are passed by reference and
structs passed by value. Swift’s built-in data types like Array and
Dictionary, are implemented as structs.
Meaning that the above getter returns a copy of [Photo] array.
Then, that same article tries to make the getter thread-safe by refactoring the code to:
fileprivate let concurrentPhotoQueue = DispatchQueue(label: "com.raywenderlich.GooglyPuff.photoQueue",
attributes: .concurrent)
fileprivate var _photos: [Photo] = []
var photos: [Photo] {
var photosCopy: [Photo]!
concurrentPhotoQueue.sync {
photosCopy = self._photos
}
return photosCopy
}
The above code explictly make a copy of self._photos in getter.
My questions are:
If by default swift already return an copy (pass by value) like the article said in the first place, why the article copy again to photosCopy to make it thread-safe? I feel myself do not fully understand these two parts mentioned in that article.
Does Swift3 really pass by value by default for Array instance like the article says?
Could someone please clarify it for me? Thanks!
I'll address your questions in reverse:
Does Swift3 really pass by value by default for Array instance like the article says?
Simple Answer: Yes
But I'm guessing that is not what your concern is when asking "Does Swift3 really pass by value". Swift behaves as if the array is copied in its entirety but behind the scenes it optimises the operation and the whole array is not copied until, and if, it needs to be. Swift uses an optimisation known as copy-on-write (COW).
However for the Swift programmer how the copy is done is not so important as the semantics of the operation - which is that after an assignment/copy the two arrays are independent and mutating one does not effect the other.
If by default swift already return an copy (pass by value) like the article said in the first place, why the article copy again to photosCopy to make it thread-safe? I feel myself do not fully understand these two parts mentioned in that article.
What this code is doing is insuring that the copy is done in a thread-safe way.
An array is not a trivial value, it is implemented as multi-field struct and some of those fields reference other structs and/or objects - this is needed to support an arrays ability to grow in size, etc.
In a multi-threaded system one thread could try to copy the array while another thread is trying to change the array. If these are allowed to happen at the same time then things easily can go wrong, e.g. the array could change while the copy is in progress, resulting in an invalid copy - part old value, part new value.
Swift per se is not thread safe; and in particular it will not prevent an array from being changed while a copy is being performed. The code you have addresses this by using a GCD queue so that during any alteration to the array by one thread all other writes or reads to the array in any other thread are blocked until the alteration is complete.
You might also be concerned that their are multiple copies going on here, self._photos to photoCopy, then photoCopy to the return value. While semantically this is what happens in practice there will probably only be one complex copy (and that will be thread safe) as the Swift system will optimise.
HTH
1) In code example what you provided will be returned copy of _photos.
As wrote in article:
The getter for this property is termed a read method as it’s reading
the mutable array. The caller gets a copy of the array and is protected
against mutating the original array inappropriately.
that's mean what you can access to _photos from outside of class, but you can't change them from there. Values of photos could be changed only inside class what make this array protected from it accidental changing.
2)Yes, Array is a value-type struct and it will be passed by value. You can easily check it in Playground
let arrayA = [1, 2, 3]
var arrayB = arrayA
arrayB[1] = 4 //change second value of arrayB
print(arrayA) //but arrayA didn't change
UPD #1
In article they have method func addPhoto(_ photo: Photo) what add new photo to _photos array what makes access to this property not thread-safe. That's mean what value of _photos could be changed on few thread in same time what will lead to issues.
They fixed it by writing photos on concurrentQueue with .barrier what make it thread-safely, _photos array will changed once per time
func addPhoto(_ photo: Photo) {
concurrentPhotoQueue.async(flags: .barrier) { // 1
self._photos.append(photo) // 2
DispatchQueue.main.async { // 3
self.postContentAddedNotification()
}
}
}
Now for ensure thread safety you need to read of _photos array on same queue. That's only reason why they refactored read method

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!

Swift let is mutable in classes why?

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.

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.