Swift: accessing computed property through pointer - swift

I was playing around with trying to get/set a computed property of an object through a pointer to that property. I've included the code snippet and output below.
The gist of the snippet is that there's a class Foo with a computed property bar. The Mutator class keeps a pointer and has a computed property value that just gets/sets the value it points to. So, if I create f1: Foo and then a m1: Mutator object that references that f1.bar, I would think that setting m1.value would also set f1.bar1. It works sometimes, but not always.
//---------------------------------------------------------------------------
// Class definitions
class Foo
{
private var data = [String: Double]()
var bar: Double?
{
get { return self.data["bar"] }
set { self.data["bar"] = newValue }
}
init(_ key: String, _ val: Double)
{
self.data[key] = val
}
}
class Mutator
{
let name: String
let storage: UnsafeMutablePointer<Double?>
var value: Double?
{
get { return self.storage.pointee }
set { self.storage.pointee = newValue}
}
init(name: String, storage: UnsafeMutablePointer<Double?>)
{
self.name = name
self.storage = storage
}
}
//---------------------------------------------------------------------------
// Create and display mutators directly
print("-\nCreate and display mutator directly")
let f1 = Foo("bar", 1.1)
let f2 = Foo("bar", 2.2)
let f3 = Foo("bar", 3.3)
let m1 = Mutator(name:"mf1", storage: &f1.bar) // Or, let m1 = Mutator(name:"f1", storage: UnsafeMutablePointer<Double?>(&f1.bar))
let m2 = Mutator(name:"mf2", storage: &f2.bar)
let m3 = Mutator(name:"mf3", storage: &f3.bar)
var before = m1.value
m1.value = 199.1
var after = m1.value
print("\(m1.name): before=\(before), after=\(after) # \(m1.storage)")
before = m2.value
m2.value = 299.2
after = m2.value
print("\(m2.name): before=\(before), after=\(after) # \(m2.storage)")
before = m3.value
m3.value = 299.2
after = m3.value
print("\(m3.name): before=\(before), after=\(after) # \(m3.storage)")
//---------------------------------------------------------------------------
// Create mutators inside function
func createMutators() -> [Mutator]
{
print("-\nIn createMutators function ...")
let m1 = Mutator(name:"mf1", storage: &f1.bar)
let m2 = Mutator(name:"mf2", storage: &f2.bar)
let m3 = Mutator(name:"mf3", storage: &f3.bar)
print("\(m1.name)=\(m1.value) # \(m1.storage)")
print("\(m2.name)=\(m2.value) # \(m2.storage)")
print("\(m3.name)=\(m3.value) # \(m3.storage)")
return [m1, m2, m3]
}
let mutator = createMutators()
//---------------------------------------------------------------------------
// Display mutators returned by function
print("-\nDisplay mutator returned by function")
for m in mutator
{
let before = m.value
m.value = 10.0 + (before ?? Double.nan)
let after = m.value
print("\(m.name): before=\(before), after=\(after) # \(m.storage)")
}
If I run the above code on Linux, I get the following output:
Create and display mutator directly
mf1: before=Optional(1.1000000000000001), after=Optional(199.09999999999999) # 0x00007ffd38f82730
mf2: before=Optional(2.2000000000000002), after=Optional(299.19999999999999) # 0x00007ffd38f82708
mf3: before=Optional(3.2999999999999998), after=Optional(299.19999999999999) # 0x00007ffd38f826e0
-
In createMutators function ...
mf1=Optional(1.1000000000000001) # 0x00007ffd38f82288
mf2=Optional(2.2000000000000002) # 0x00007ffd38f82260
mf3=Optional(3.2999999999999998) # 0x00007ffd38f82238
-
Display mutator returned by function
mf1: before=Optional(4.9406564584124654e-324), after=Optional(10.0) # 0x00007ffd38f82288
mf2: before=Optional(6.9527664311957093e-310), after=Optional(10.0) # 0x00007ffd38f82260
mf3: before=nil, after=Optional(nan) # 0x00007ffd38f82238
The first output block shows expected behavior. The second block points to a different address, which is unexpected. Weirder still is that despite having the wrong address, it reads the correct values. The last output block has the same address as in the second block, but reads different initial values, though it does manage to set and read back the values correctly.
I know this is perhaps an abuse of the computed properties and pointers. But can anyone explain why it works sometimes? Why does creating it in the function give it a different address? Why would reading it in the function and after it's returned give different answers when the addresses are the same? Is there a way to make this work?
Just to confuse things further: the above was from running on Linux. When I try this experiment on a mac I get somewhat different results, though the overall observation that sometimes it works remains true.

None of this is defined behaviour. It may or may not produce expected results, or it may just crash at runtime.
When you say
let m1 = Mutator(name:"mf1", storage: &f1.bar)
Swift will allocate some memory and initialise it to the value returned by f1.bar's getter. A pointer to this memory will then be passed into Mutator's init – and after the call, Swift will then call f1.bar's setter with the (possibly changed) contents of the memory it allocated.
This memory will then be deallocated – the pointer is now no longer valid. Reading and writing to its pointee will produce undefined behaviour. Therefore, you should not persist the pointer after the call to Mutator's initialiser.
One way in order to get the behaviour you want is to use two closures for the getting and setting of f1.bar, both capturing f1. This ensures that the reference to f1 remains valid as long as the closures live.
For example:
struct Mutator<T> {
let getter: () -> T
let setter: (T) -> Void
var value: T {
get {
return getter()
}
nonmutating set {
setter(newValue)
}
}
init(getter: #escaping () -> T, setter: #escaping (T) -> Void) {
self.getter = getter
self.setter = setter
}
}
You can then use it like so:
class Foo {
private var data = [String : Double]()
var bar: Double? {
get { return self.data["bar"] }
set { self.data["bar"] = newValue }
}
init(_ key: String, _ val: Double) {
self.data[key] = val
}
}
let f1 = Foo("bar", 1.1)
let m1 = Mutator(getter: { f1.bar }, setter: { f1.bar = $0 })
let before = m1.value
m1.value = 199.1
print("m1: before = \(before as Optional), after = \(m1.value as Optional)")
print("f1 after = \(f1.bar as Optional)")
// m1: before = Optional(1.1000000000000001), after = Optional(199.09999999999999)
// f1 after = Optional(199.09999999999999)
Although one downside to this approach is the repetition of value you're getting and setting (f1.bar in this case). One alternative implementation would be to use a single closure with a function argument that takes an inout parameter, returning the (possibly mutated) value.
struct Mutator<T> {
let getter: () -> T
let setter: (T) -> Void
var value: T {
get {
return getter()
}
nonmutating set {
setter(newValue)
}
}
init(mutator: #escaping ((inout T) -> T) -> T) {
// a function, which when applied, will call mutator with a function input
// that just returns the inout argument passed by the caller.
getter = {
mutator { $0 }
}
// a function, which when applied with a given new value, will call mutator
// with a function that will set the inout argument passed by the caller
// to the new value, which will then be returned
// (but ignored by the outer function)
setter = { newValue in
_ = mutator { $0 = newValue; return $0 }
}
}
}
// ...
let f1 = Foo("bar", 1.1)
let m1 = Mutator { $0(&f1.bar) }
The getter now simply applies the passed function, returning the inout parameter passed (f1.bar in this case), and the setter uses this inout parameter in order to assign a new value.
Although personally, I prefer the first approach, despite the repetition.

The Swift language definition does not require it to not move (or reuse) the memory used by class object instance properties, once outside the block or scope where unsafe pointers and other internal references are valid.
So, in your second and third case, the object (or some of its properties) has likely been moved and you are examining and (dangerously) changing memory where the object used to be, and where part of some completely different type of object might currently be, through a stale (and thus very unsafe) pointer.
So the Swift compiler (which knows when and where it moved stuff) knows how to read and write the property inside the instance. But you (via stale pointers) do not.
Added: If you want to do this type of stuff, then allocate (and manage) the memory yourself (which is possible in Swift).

Related

Can I set computed property in get block in Swift?

var myProperty: PropertyType {
get {
if let alreadyComupted = savedValue {
return alreadyComputed
}
return computeAndSave(someParam: "Hello")
}
set {
// is it possible to move *computeAndSave* body here somehow so I can call *set* method in above get definition?
}
}
private var savedValue: PropertyType?
private func computeAndSave(someParam: SomeType) -> PropertyType {
// perform computations and assign value to *savedValue*
}
I am fairly new to swift language, not sure if this is even standard by coding practice or not.
Basically you are describing a lazy variable. It calculates its initializer once, when the value is first fetched, and from then on uses the stored value (unless it is replaced). You can combine this with a define-and-call initializer:
lazy var myProperty: PropertyType = {
let p = // perform some expensive one-time calculation here
return p
}()
The outcome is that the first time you ask for the value of myProperty, the initializer method runs; but after that the previous result is used, and the initializer method never runs again.

Swift memoizing/caching lazy variable in a struct

I drank the struct/value koolaid in Swift. And now I have an interesting problem I don't know how to solve. I have a struct which is a container, e.g.
struct Foo {
var bars:[Bar]
}
As I make edits to this, I create copies so that I can keep an undo stack. So far so good. Just like the good tutorials showed. There are some derived attributes that I use with this guy though:
struct Foo {
var bars:[Bar]
var derivedValue:Int {
...
}
}
In recent profiling, I noticed a) that the computation to compute derivedValue is kind of expensive/redundant b) not always necessary to compute in a variety of use cases.
In my classic OOP way, I would make this a memoizing/lazy variable. Basically, have it be nil until called upon, compute it once and store it, and return said result on future calls. Since I'm following a "make copies to edit" pattern, the invariant wouldn't be broken.
But I can't figure out how to apply this pattern if it is struct. I can do this:
struct Foo {
var bars:[Bar]
lazy var derivedValue:Int = self.computeDerivation()
}
which works, until the struct references that value itself, e.g.
struct Foo {
var bars:[Bar]
lazy var derivedValue:Int = self.computeDerivation()
fun anotherDerivedComputation() {
return self.derivedValue / 2
}
}
At this point, the compiler complains because anotherDerivedComputation is causing a change to the receiver and therefore needs to be marked mutating. That just feels wrong to make an accessor be marked mutating. But for grins, I try it, but that creates a new raft of problems. Now anywhere where I have an expression like
XCTAssertEqaul(foo.anotherDerivedComputation(), 20)
the compiler complains because a parameter is implicitly a non mutating let value, not a var.
Is there a pattern I'm missing for having a struct with a deferred/lazy/cached member?
Memoization doesn't happen inside the struct. The way to memoize is to store a dictionary off in some separate space. The key is whatever goes into deriving the value and the value is the value, calculated once. You could make it a static of the struct type, just as a way of namespacing it.
struct S {
static var memo = [Int:Int]()
var i : Int
var square : Int {
if let result = S.memo[i] {return result}
print("calculating")
let newresult = i*i // pretend that's expensive
S.memo[i] = newresult
return newresult
}
}
var s = S(i:2)
s.square // calculating
s = S(i:2)
s.square // [nothing]
s = S(i:3)
s.square // calculating
The only way I know to make this work is to wrap the lazy member in a class. That way, the struct containing the reference to the object can remain immutable while the object itself can be mutated.
I wrote a blog post about this topic a few years ago: Lazy Properties in Structs. It goes into a lot more detail on the specifics and suggest two different approaches for the design of the wrapper class, depending on whether the lazy member needs instance information from the struct to compute the cached value or not.
I generalized the problem to a simpler one: An x,y Point struct, that wants to lazily compute/cache the value for r(adius). I went with the ref wrapper around a block closure and came up with the following. I call it a "Once" block.
import Foundation
class Once<Input,Output> {
let block:(Input)->Output
private var cache:Output? = nil
init(_ block:#escaping (Input)->Output) {
self.block = block
}
func once(_ input:Input) -> Output {
if self.cache == nil {
self.cache = self.block(input)
}
return self.cache!
}
}
struct Point {
let x:Float
let y:Float
private let rOnce:Once<Point,Float> = Once {myself in myself.computeRadius()}
init(x:Float, y:Float) {
self.x = x
self.y = y
}
var r:Float {
return self.rOnce.once(self)
}
func computeRadius() -> Float {
return sqrtf((self.x * self.x) + (self.y * self.y))
}
}
let p = Point(x: 30, y: 40)
print("p.r \(p.r)")
I made the choice to have the OnceBlock take an input, because otherwise initializing it as a function that has a reference to self is a pain because self doesn't exist yet at initialization, so it was easier to just defer that linkage to the cache/call site (the var r:Float)

Swift: How to assign a variable by reference, not by value?

I'm trying to get a reference to an Array and make modifications to it. Because Arrays in Swift are value types, instead of reference types, if I assign my array to a variable first, I am getting a copy of the array instead of the actual array:
var odds = ["1", "3", "5"]
var evens = ["2", "4", "6"]
var source = odds
var destination = evens
var one = odds.first!
source.removeFirst() // only removes the first element of the `source` array, not the `odds` array
destination.append(one)
When we look at the odds and evens arrays, they are unaltered because we changed the source and destination arrays.
I know that I can use the inout parameter attribute on a function to pass them by reference, instead of by value:
func move(inout source: [String], inout destination: [String], value:String) {
source.removeAtIndex(source.indexOf(value)!)
destination.append(value)
}
move(&odds, destination: &evens, value:one)
Is there a way to assign these arrays to a variable by reference, instead of by value?
Array is a struct, which means it's a value type in Swift. Because of this, arrays always behave according to value and not reference semantics. The problem here is that you're attempting to use mutable, reference based logic to operate on values types.
You don't want to rely on mutations occurring inside the function to propagate back to the caller. As you've found, this is only possible with inout parameters. What you should do instead is return the mutated array from the function back to the caller. The point of value oriented programming is that it shouldn't matter which array you have, but rather that any two equivalent arrays or values types are interchangeable.
It's slightly easier to imagine with another value type. Take an Int for example, and this function that does some math.
func addFive(int: Int) -> Int {
return int + 5
}
Now consider a similar function, but written in the reference oriented style that you're attempting to use:
func addFive(inout int: Int) {
int = int + 5
}
You can see it's simply not natural to operate on value types this way. Instead just return the updated value (the modified arrays) from your function and carry on from there.
Here is your function refactored with value semantics.
func move(source: [String], destination: [String], value:String) -> ([String], [String]) {
var mutableSource = source
var mutableDestination = destination
mutableSource.removeAtIndex(source.indexOf(value)!)
mutableDestination.append(value)
return (mutableSource, mutableDestination)
}
let (updatedSource, updatedDestination) = move(odds, destination: evens, value:one)
You cannot assign an array to a variable by reference in Swift.
"In Swift, Array, String, and Dictionary are all value types..."
Source: https://developer.apple.com/swift/blog/?id=10
If you need arrays that can be manipulated by reference you can create a class that encapsulates an array and use it for your variables.
here's an example:
class ArrayRef<Element>:CustomStringConvertible
{
var array:[Element]=[]
init() {}
init(Type:Element.Type) {}
init(fromArray:[Element]) { array = fromArray }
init(_ values:Element ...) { array = values }
var count:Int { return array.count }
// allow use of subscripts to manipulate elements
subscript (index:Int) -> Element
{
get { return array[index] }
set { array[index] = newValue }
}
// allow short syntax to access array content
// example: myArrayRef[].map({ $0 + "*" })
subscript () -> [Element]
{
get { return array }
set { array = newValue }
}
// allow printing the array example: print(myArrayRef)
var description:String { return "\(array)" }
// delegate append method to internal array
func append(newElement: Element)
{ array.append(newElement) }
// add more delegation to array methods as needed ...
}
// being an object, the ArrayRef class is always passed as a reference
func modifyArray(X:ArrayRef<String>)
{
X[2] = "Modified"
}
var a = ArrayRef("A","B","C")
modifyArray(a)
print(a) // --> a is now ["A", "B", "Modified"]
// various means of declaration ...
var b = ArrayRef<String>()
b[] = ["1","2","3"]
var c = ArrayRef(fromArray:b[])
// use .array to modify array content (or implement delegation in the class)
c.array += a[] + ["X","Y","Z"]
Note that you could also define your arrays as NSMutableArrays which are Obj-C classes and are passed by reference. It does a similar thing and does present differences with a regular array for the methods that are available.
I recommend this the following only for didactic purpose only, I advise against using it in production code.
You can circulate a "reference" to something via an UnsafePointer instance.
class Ref<T> {
private var ptr: UnsafePointer<T>!
var value: T { return ptr.pointee }
init(_ value: inout T) {
withUnsafePointer(to: &value) { ptr = $0 }
}
}
var a = ["1"]
var ref = Ref(&a)
print(a, ref.value) // ["1"] ["1"]
a.append("2")
print(a, ref.value) // ["1", "2"] ["1", "2"]
ref.value.removeFirst()
print(a, ref.value) // ["2"] ["2"]
Thus, you can simulate a reference to a variable via the above class, which stores a pointer to the given variable reference.
Please note that this is a simple use case, and will behave as expected only if if the variable doesn't get destroyed before the pointer, as in that case the memory initially occupied by the variable will be replaced by something else, and the unsafe pointer will no longer be valid. Take for example the next code:
var ref: Ref<[String]>!
// adding an inner scope to simulate `a` being destroyed
do {
var a: [String] = ["a"]
ref = Ref(&a)
print(a, ref.value)
a = ["b"]
print(a, ref.value)
}
// `a` was destroyed, however it's place on the stack was taken by `b`
var b: [String:Int] = ["a": 1]
// however `b` is not an array, thus the next line will crash
print(ref.value)

Re-initialize a lazy initialized variable in Swift

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 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 structs 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.
EDIT: As per Ben Leggiero's answer, lazy vars can be nilable in Swift 3.
EDIT 2: Seems like nilable lazy vars are no more.
Very late to the party, and not even sure if this will be relevant in Swift 3, but here goes. David's answer is good, but if you want to create many lazy nil-able vars, you will have to write a pretty hefty block of code. I'm trying to create an ADT that encapsulates this behaviour. Here's what I've got so far:
struct ClearableLazy<T> {
private var t: T!
private var constructor: () -> T
init(_ constructor: #escaping () -> T) {
self.constructor = constructor
}
mutating func get() -> T {
if t == nil {
t = constructor()
}
return t
}
mutating func clear() { t = nil }
}
You would then declare and use properties like this:
var aClient = ClearableLazy(Client.init)
aClient.get().delegate = self
aClient.clear()
There are things I don't like about this yet, but don't know how to improve:
You have to pass a constructor to the initializer, which looks ugly. It has the advantage, though, that you can specify exactly how new objects are to be created.
Calling get() on a property every time you want to use it is terrible. It would be slightly better if this was a computed property, not a function, but computed properties cannot be mutating.
To eliminate the need to call get(), you have to extend every type you want to use this for with initializers for ClearableLazy.
If someone feels like picking it up from here, that would be awesome.
This allows setting the property to nil to force reinitialization:
private var _recordedFileURL: NSURL!
/// Location of the recorded file
private var recordedFileURL: NSURL! {
if _recordedFileURL == nil {
let file = "recording\(arc4random()).caf"
let url = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent(file)
NSLog("FDSoundActivatedRecorder opened recording file: %#", url)
_recordedFileURL = url
}
return _recordedFileURL
}
Swift 5.1:
class Game {
private var _scores: [Double]? = nil
var scores: [Double] {
if _scores == nil {
print("Computing scores...")
_scores = [Double](repeating: 0, count: 3)
}
return _scores!
}
func resetScores() {
_scores = nil
}
}
Here is how to use:
var game = Game()
print(game.scores)
print(game.scores)
game.resetScores()
print(game.scores)
print(game.scores)
This produces the following output:
Computing scores...
[0.0, 0.0, 0.0]
[0.0, 0.0, 0.0]
Computing scores...
[0.0, 0.0, 0.0]
[0.0, 0.0, 0.0]
Swift 5.1 and Property Wrapper
#propertyWrapper
class Cached<Value: Codable> : Codable {
var cachedValue: Value?
var setter: (() -> Value)?
// Remove if you don't need your Value to be Codable
enum CodingKeys: String, CodingKey {
case cachedValue
}
init(setter: #escaping () -> Value) {
self.setter = setter
}
var wrappedValue: Value {
get {
if cachedValue == nil {
cachedValue = setter!()
}
return cachedValue!
}
set { cachedValue = nil }
}
}
class Game {
#Cached(setter: {
print("Computing scores...")
return [Double](repeating: 0, count: 3)
})
var scores: [Double]
}
We reset the cache by setting it to any value:
var game = Game()
print(game.scores)
print(game.scores)
game.scores = []
print(game.scores)
print(game.scores)
There are some good answers here. Resetting a lazy var is indeed, desirable in a lot of cases.
I think, you can also define a closure to create client and reset lazy var with this closure. Something like this:
class ClientSession {
class func shared() -> ClientSession {
return ClientSession()
}
}
class Client {
let session:ClientSession
init(_ session:ClientSession) {
self.session = session
}
}
class Test {
private let createClient = {()->(Client) in
var _aClient = Client(ClientSession.shared())
print("creating client")
return _aClient
}
lazy var aClient:Client = createClient()
func resetClient() {
self.aClient = createClient()
}
}
let test = Test()
test.aClient // creating client
test.aClient
// reset client
test.resetClient() // creating client
test.aClient
If the objective is to re-initialize a lazy property but not necessarily set it to nil, Building from Phlippie Bosman and Ben Leggiero, here is something that avoids conditional checks every time the value is read:
public struct RLazy<T> {
public var value: T
private var block: () -> T
public init(_ block: #escaping () -> T) {
self.block = block
self.value = block()
}
public mutating func reset() {
value = block()
}
}
To test:
var prefix = "a"
var test = RLazy { () -> String in
return "\(prefix)b"
}
test.value // "ab"
test.value = "c" // Changing value
test.value // "c"
prefix = "d"
test.reset() // Resetting value by executing block again
test.value // "db"
I made #David Berry's answer into a property wrapper. Works great with UI-components that you want to reload if you need to apply size changes but otherwise want to hold in their configured state.
#propertyWrapper class Reloadable<T: AnyObject> {
private let initializer: (() -> T)
private var _wrappedValue: T?
var wrappedValue: T {
if _wrappedValue == nil {
_wrappedValue = initializer()
}
return _wrappedValue!
}
init(initializer: #escaping (() -> T)) {
self.initializer = initializer
}
func nuke() {
_wrappedValue = nil
}
}
Here's an example with a CAShapeLayer. Set you variable like so:
#Reloadable<CAShapeLayer>(initializer: {
Factory.ShapeLayer.make(fromType: .circle(radius: Definitions.radius, borderWidth: Definitions.borderWidth)) // this factory call is just what I use personally to build my components
}) private var circleLayer
and when you want to reload your view just call:
_circleLayer.nuke()
Then you can use the var circleLayer as you normally would in your re-layout routine, upon which it will get re-initialized.
PS: I made a gist for the file I use in my own project: https://gist.github.com/erikmartens/b34a130d11b62400ab13a59a6c3dbd91
This seems like a pretty bad code smell. Something strange is going on with:
var _aClient = Clinet(ClinetSession.shared())
What is ClinetSession.shared()?
This looks like a static function that returns a new instance of ClinetSession on every call.
So no wonder you are not seeing changes to this object. It seems like a broken singleton to me.
class ClinetSession {
static func shared() -> Self {
ClinetSession()
}
}
Try doing this instead:
class ClinetSession {
static let shared = ClinetSession()
var value: Int = 0
}
Now if you change ClinetSession value you will see it.
There's no need for a lazy property that can be reset here as far as I can tell.
Here's a fully working example. BTW if you don't own ClinetSession then write a wrapper to control this.
class Clinet {
let clinetSession: ClinetSession
init(_ clinetSession: ClinetSession) {
self.clinetSession = clinetSession
}
var delegate: P?
}
class ClinetSession {
static let shared = ClinetSession()
var value = 0
}
protocol P { }
struct Test: P {
lazy var aClient:Clinet = {
var _aClient = Clinet(ClinetSession.shared)
_aClient.delegate = self
return _aClient
}()
mutating func updateSession() {
aClient.clinetSession.value = 10
}
}
var test = Test()
test.updateSession()
print(test.aClient.clinetSession.value)
// prints 10
Note: If you don't want to use a singleton then don't use shared() as the constructor as this is a convention. But then it's up to you to make sure you pass in the same reference as the one you want to mutate. That's your job to manage. The singleton just makes sure there is only 1 instance so this becomes simpler but has its trade offs.

Assigning to a var defined in Protocol says that it can't assign to it

Here is a simple use case:
protocol Debuggable {
var _d: String -> () {
get set
}
}
class Debugger {
class func attach(instance: Debuggable) {
let d = Debugger()
instance._d = d.d // This doesn't work
}
func d(message: String) {
println(message)
}
}
Error message at compile time: Cannot assign to _d in instance, although my protocol defines the var with a getter and setter.
This is a good case of the compiler catching an error for you :)
instance may be a value type and therefore be copied in to this method. In that case, you will modify the copy and just throw it out. The quickest fix is to define it as an inout parameter:
class func attach(inout instance: Debuggable) {
let d = Debugger()
instance._d = d.d
}
I think it would be better to return the instance though:
class func attach(instance: Debuggable) -> Debuggable {
let d = Debugger()
var ret = instance
ret._d = d.d
return ret
}
I feel that inout is mostly in the language for backwards compatibility to Objective-C and C. Let the type determine if it should be copied instead of forcing it to be passed by reference. If something is defined as a value type, there is a good reason for it.