I have a variable that initialized as:
lazy var aClient:Clinet = { var _aClient = Clinet(ClinetSession.shared()) _aClient.delegate = self return _aClient }()
The problem is, at some point, I need to reset this aClient
variable so it can initialize again when the ClinetSession.shared()
changed. But if I set the class to optional Clinet?
, LLVM will give me an error when I try to set it to nil
. If I just reset it somewhere in the code using aClient = Clinet(ClinetSession.shared())
, it will end up with EXEC_BAD_ACCESS
.
Is there a way that can use lazy
and being allowed to reset itself?
lazy initialisation is a delegation of object creation when the first time that object will be called. The reference will be created but the object will not be created. The object will only be created when the first time that object will be accessed and every next time the same reference will be used.
Constant properties must always have a value before initialization completes, and therefore cannot be declared as lazy.
You cannot declare a lazy variable anywhere in the code. To make a variable lazy, use the keyword modifier lazy in front of var. Instead of directly assigning a value to the lazy variable, it has to be computed. The computation takes place in a block of code.
All of a class's stored properties—including any properties the class inherits from its superclass—must be assigned an initial value during initialization. Swift defines two kinds of initializers for class types to help ensure all stored properties receive an initial value.
lazy is explicitly for one-time only initialization. The model you want to adopt is probably just an initialize-on-demand model:
var aClient:Client { if(_aClient == nil) { _aClient = Client(ClientSession.shared()) } return _aClient! } var _aClient:Client?
Now whenever _aClient
is nil
, it will be initialized and returned. It can be reinitialized by setting _aClient = nil
Because the behavior of lazy
changed in Swift 4, I wrote a few struct
s that give very specific behavior, which should never change between language versions. I put these on GitHub, under the BH-1-PD license: https://github.com/RougeWare/Swift-Lazy-Patterns
ResettableLazy
Here is the one relevant to this question, which gives you a way to lazily-initialize a value, cache that value, and destroy it so it can be lazily-reinitialized later.
Note that this requires Swift 5.1! For the Swift 4 version, see version 1.1.1 of that repo.
The simple usage of this is very straightforward:
@ResettableLazy var myLazyString = "Hello, lazy!" print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!" print(myLazyString) // Just returns the value "Hello, lazy!" _myLazyString.clear() print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!" print(myLazyString) // Just returns the value "Hello, lazy!" myLazyString = "Overwritten" print(myLazyString) // Just returns the value "Overwritten" _myLazyString.clear() print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
This will print:
Hello, lazy! Hello, lazy! Hello, lazy! Hello, lazy! Overwritten Hello, lazy!
If you have complex initializer logic, you can pass that to the property wrapper:
func makeLazyString() -> String { print("Initializer side-effect") return "Hello, lazy!" } @ResettableLazy(initializer: makeLazyString) var myLazyString: String print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!" print(myLazyString) // Just returns the value "Hello, lazy!" _myLazyString.clear() print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!" print(myLazyString) // Just returns the value "Hello, lazy!" myLazyString = "Overwritten" print(myLazyString) // Just returns the value "Overwritten" _myLazyString.clear() print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
You can also use it directly (instaed of as a property wrapper):
var myLazyString = ResettableLazy<String>() { print("Initializer side-effect") return "Hello, lazy!" } print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!" print(myLazyString.wrappedValue) // Just returns the value "Hello, lazy!" myLazyString.clear() print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!" print(myLazyString.wrappedValue) // Just returns the value "Hello, lazy!" myLazyString.wrappedValue = "Overwritten" print(myLazyString.wrappedValue) // Just returns the value "Overwritten" _myLazyString.clear() print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
These will both print:
Initializer side-effect Hello, lazy! Hello, lazy! Initializer side-effect Hello, lazy! Hello, lazy! Overwritten Initializer side-effect Hello, lazy!
This answer has been updated; its original solution no longer works in Swift 4 and newer.
Instead, I recommend you use one of the solutions listed above, or @PBosman's solution
Previously, this answer hinged on behavior which was a bug. Both that old version of this answer, its behavior, and why it's a bug are described in the text and comments of Swift bug SR-5172 (which has been resolved as of 2017-07-14 with PR #10,911), and it's clear that this behavior was never intentional.
That solution is in that Swift bug's text, and also in the history of this answer, but because it's a bug exploit that doesn't work in Swift 3.2+ I recommend you do not do that.
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