Skip to content

Instantly share code, notes, and snippets.

@jpmcglone
Last active August 18, 2020 20:41
Show Gist options
  • Save jpmcglone/87883ff281408731256251e32f98ad9c to your computer and use it in GitHub Desktop.
Save jpmcglone/87883ff281408731256251e32f98ad9c to your computer and use it in GitHub Desktop.
@propertyWrapper
class Lazy<T> {
fileprivate var loader: (()->T)
fileprivate var _value: T?
var projectedValue: Lazy<T> { return self }
var wrappedValue: T {
_value = _value ?? loader()
return _value!
}
init(wrappedValue: T) {
_value = wrappedValue
self.loader = { wrappedValue }
}
init(_ loader: @autoclosure @escaping ()->T) {
self.loader = loader
}
init(load loader: @escaping ()->T) {
self.loader = loader
}
func reset() {
_value = nil
}
}
@propertyWrapper
class LazyMutable<T>: Lazy<T> {
override var projectedValue: LazyMutable<T> { return self }
override var wrappedValue: T {
get { super.wrappedValue }
set { set(newValue) }
}
func set(_ loader: @autoclosure @escaping ()->T) {
set(loader)
}
func set(_ loader: @escaping ()->T) {
_value = nil
self.loader = loader
}
}
@jpmcglone
Copy link
Author

jpmcglone commented Dec 31, 2019

Usage

// 1a. Lazy property wrapper
@Lazy var x = "ABC"

print(x) // ABC, evaluated
print(x) // ABC, non-evaluated
print(x) // ABC, non-evaluated

// 1b. LazyMutable property wrapper
@LazyMutable var xx = "ABC"

print(xx) // ABC, evaluated
print(xx) // ABC, non-evaluated
print(xx) // ABC, non-evaluated

xx = "XYZ"

print(xx) // XYZ, evaluated
print(xx) // XYZ, non-evaluated
print(xx) // XYZ, non-evaluated

// 2. Lazy with closure
var y = Lazy {
  // Some evaluation code
  return "ABC"
}

print(y) // ABC, evaluated
print(y) // ABC, non-evaluated

y.reset()

print(y) // ABC, evaluated

// 3. Lazy Mutable, non-closure
var z = LazyMutable("ABC")

print(z) // ABC, evaluated
print(z) // ABC, non-evaluated

z.reset()

print(z) // ABC, evaluated
print(z) // ABC, non-evaluated
print(z) // ABC, non-evaluated

z = "XYZ" 

print(z) // XYZ, evaluated
print(z) // XYZ, non-evaluated

z.reset()

print(z) // XYZ, evaluated
print(z) // XYZ, non-evaluated

z.set {
  // Some evaluation code
  return "LMNOP"
}

print(z) // LMNOP, evaluated
print(z) // LMNOP, non-evaluated

z.set("QRS") 

print(z) // QRS, evaluated
print(z) // QRS, non-evaluated

@jpmcglone
Copy link
Author

jpmcglone commented Dec 31, 2019

Here's a struct approach:

// MARK: - Property Wrappers / Structs
@propertyWrapper
struct Lazy<T> {
  private var loader: (()->T)
  private var _value: T?
  var wrappedValue: T {
    mutating get { get() }
  }

  init(_ loader: @autoclosure @escaping ()->T) {
    self.loader = loader
  }

  init(wrappedValue: T) {
    self.init(wrappedValue)
  }

  mutating func get() -> T {
    _value = _value ?? loader()
    return _value!
  }

  mutating func reset() {
    _value = nil
  }
}

@propertyWrapper
struct LazyMutable<T> {
  private var loader: (()->T)
  private var _value: T?
  var wrappedValue: T {
    mutating get { get() }
    set { set(newValue) }
  }

  init(_ loader: @autoclosure @escaping ()->T) {
    self.loader = loader
  }

  init(wrappedValue: T) {
    self.init(wrappedValue)
  }

  mutating func get() -> T {
    _value = _value ?? loader()
    return _value!
  }

  mutating func set(_ loader: @autoclosure @escaping ()->T) {
    _value = nil
    self.loader = loader
  }

  mutating func reset() {
    _value = nil
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment