What is the difference between a Lazy
or Optional
property in Swift?
For example, if someone is building a navigation bar that comes in from the side, I think that should all be within one UIViewController
. The user might never open the menu but sometimes they will.
var menu: NavigationBar?
lazy var menu: NavigationBar = NavigationBar.initialize()
Both of the optional I think are good code, because they don't create the view unless its needed. I understand Optional
means there might be a value it might be nil
. I also understand Lazy
means don't worry about it until I need it.
Specific Question
My question is are their performance patterns (safety and speed) that say optionals are faster and safer or vise versa?
A lazy stored property is a property whose initial value isn't calculated until the first time it's used. You indicate a lazy stored property by writing the lazy modifier before its declaration.
Swift's lazy properties let us delay the creation of a property until it's actually used, which makes them like a computed property. However, unlike a computed property they store the result that gets calculated, so that subsequent accesses to the property don't redo the work.
Important to note: You can use self inside the closure of a lazy property. It will not cause any retain cycles.
There are two kinds of properties: stored properties and computed properties. Stored properties are properties that are stored in the class's instance. Stored properties store constant and variable values. Computed properties are for creating custom get and set methods for stored properties.
OK, this is an interesting question, and I don't want to imply that the existing answers aren't good, but I thought I'd offer my take on things.
lazy
variables are great for things that need to be setup once, then never re-set. It's a variable, so you could change it to be something else, but that kind of defeats the purpose of a lazy
variable (which is to set itself up upon demand).
Optionals are more for things that might go away (and might come back again). They need to be set up each time.
So let's look at two scenarios for your side menu: one where it stays around while it's not visible, and another for when it is deallocated.
lazy var sideMenu = SideMenu()
So the first time the sideMenu
property is accessed, SideMenu()
is called and it is assigned to the property. The instance stays around forever, even when you're not using it.
Now let's see another approach.
var _sideMenu: SideMenu?
var sideMenu: SideMenu! {
get {
if let sm = _sideMenu {
return sm
} else {
let sm = SideMenu()
_sideMenu = sm
return sm
}
}
set(newValue) {
_sideMenu = newValue
}
}
(Note this only works for classes, not structs.)
OK so what does this do? Well it behaves very similarly to the lazy
var, but it let's you re-set it to nil
. So if you try to access sideMenu
, you are guaranteed to get an instance (either the one that was stored in _sideMenu
or a new one). This is a similar pattern in that it lazily loads SideMenu()
but this one can create many SideMenu()
instances, where the previous example can only create one once.
Now, most view controllers are small enough that you should probably just use lazy
from earlier.
So two different approaches to the same problem. Both have benefits and drawbacks, and work better or worse in different situations.
They're actually pretty different.
Optional
means that the value could possibly be nil, and the user isn't guaranteeing that it won't be. In your example, var menu: NavigationBar?
could be nil for the entire lifetime of the class, unless something explicitly assigns it.
Lazy
on the other hand means that the assignment will not be called until it is first accessed, meaning that somewhere in code someone tries to use your object. Note however that it is STILL promised to not be nil if you declare it like you have here lazy var menu: NavigationBar = NavigationBar.initialize()
, so no need to do optional chaining.
And actually, a variable can be BOTH Lazy
AND Optional
, which means that it's value will be loaded when it is first accessed, and that value might be nil at the point it's initialized or at any future point. For example:
lazy var menu: NavigationBar? = NavigationBar.initialize()
That NavigationBar.initialize()
is now allowed to return nil, or someone in the future could set the menu
to be nil without the compiler/runtime throwing errors!
Does that make the difference clear?
Edit:
As to which is BETTER that's really a case by case thing. Lazy
variables take a performance hit on first initialization, so the first access will be a slow one if the initialization process is long. Otherwise, they're nearly identical in terms of safety/performance. Optional
variables you have to unwrap before using and so there is a very minor performance cost with that (one machine instruction, not worth the time to think about)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With