I want to create a macro to protect the initialization process of lazy varibles. For example:
@ThreadSafeLazy(lock: lock)
lazy var varible: Int = {
return 1 + 1
}()
// will expanded to
lazy var varible: Int = {
lock.around {
if let _unique_name_varible { return _unique_name_varible }
_unique_name_varible = {
return 1 + 1
}()
return _unique_name_varible
}
}()
var _unique_name_varible: Int? = nil
I know how to add a _unique_name_varible varible using PeerMacro here, but I have no idea how to override the varible's initiate closure.
- #1 Edit:
Due to the lazy var generation compiler crash issue, I changed the solution for workaround. now the macro looks like:
@Lazify(name: "internalClassName", lock: "SomeClass.classLock")
func createLazyVariable() -> String {
return "__" + NSStringFromClass(type(of: self))
}
// expanded to
private(set) var _lazy_internalClassName: String? = nil
var internalClassName: String {
if let exsist = self._lazy_internalClassName {
return exsist
}
return SomeClass.classLock.around { [weak self] in
guard let self else {
fatalError()
}
if let exsist = self._lazy_internalClassName {
return exsist
}
let temp = self.createLazyVariable()
self._lazy_internalClassName = temp
return temp
}
}
But there's another problem I encountered, I raised another topic here
You cannot modify existing declarations using a macro, except for adding accessors to a property.
As a workaround, you can make the lazy var declarations a parameter of a freestanding declaration macro. That is,
You would add an additional
() -> Voidclosure argument toThreadSafeLazy, so that users of the macro can declare lazy vars in the closure. The macro can then expand to the two declarations you want.Here is a simple implementation:
With this approach, you don't even need to write
lazyexplicitly in the variable declaration, because that can also be added byThreadSafeLazy. Though IMO that could create confusion.It is possible to declare multiple lazy vars in the closure, so you can also consider a design like what I did in this answer:
where
ThreadSafeLazyexpands to all the declaration inside the closure, but if any of the declarations has a@Lockedmacro attached,ThreadSafeLazygenerates the thread safe version of the property, along with the additional unique name property.Lockeddoesn't need to expand to anything - it only acts as a marker forThreadSafeLazy.