Using Lazy Variables to Work Around Swift Initialization Rules
In all these cases, you have to provide the object with its delegate (or target) at initialization time. And that leads to a terrible head-scratcher. What do you do if you want to use one of these objects as a property, but also use
self
as the delegate?Swift won’t let you easily do this.
[…]
The point of the
lazy
variable is that it won’t be assigned until its needed. Which in turn means that it can’t be accessed untilself
exists, because otherwise there’s no way to access it. Therefore, it’s always OK to referenceself
when assigning alazy
variable.
I understand the reasoning behind Swift’s rules for two-phase initialization and where you can access self
. But in practice they introduce extra work and code ugliness in order to prevent a class of problems that I don’t think I’ve ever had. lazy
variables are the best solution I’ve found for the issue that Adamson mentions. In fact, lazy
variables are pretty cool in general, although beware that their initialization is not thread-safe.
The other main issue I run into with initialization is that you can’t call helper methods before calling super.init()
. This is not an issue in Objective-C because, there, calling [super init]
is the first thing you do. But in Swift you have to initialize all of your properties before calling super.init()
. Implicitly-unwrapped optionals are the obvious, easy solution. Otherwise, you would need to move helper code from instance methods to static functions or a separate helper object or, in some cases, completely restructure your code if you need to access state or functionality from the superclass.
Update (2017-08-31): One of the issues that neither lazy
nor IUOs solve is that you may end up needing to make the property var
instead of let
, even though you have no intention of modifying it once set. You can partially mitigate this by marking the property private(set)
. However, that adds yet more verbosity and doesn’t as obviously protect against concurrent access the way let
does.
Regarding lazy
and concurrency, Heath Borders writes:
I just use another computed property that wraps the
lazy var
in aDispatch.sync
over a private queue, then I never touch thelazy var
.
Update (2017-09-01): Another issue with IUOs is the way they work with type inference. If you assign an IUO to a temporary variable, the inferred type is an optional. This means that you have to either force unwrap when assigning to the temporary or at each use of the temporary. So the IUO has leaked outside of its class. One way around this is to create two properties in the class. The first is a private var
of type T!
. The second is a computed property of type T
that returns the first property (implicitly) unwrapped. Then clients of the class can use the computed property and not have to deal with unwrapping.
Previously: