You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The @Lazy and @LazyConstant property wrappers do not appear to be thread safe. That is, if two threads attempt to hit the initial getter at the exact same time, they will both attempt to initialize the value. Running the initializer multiple times can have the adverse effect of executing the "expensive operation" multiple times, which is not a good thing.
Note: This only would happen if the @Lazy objects are accessed in multiple threads. For UIKit initializers, everything must be done on the main (synchronous) thread, and so we don't have this issue.
The "standard" solution for such a case is creating a concurrent queue, and doing the work within the getter in a queue.sync{} block, and (for the non-constant @Lazy implementation), putting the setter into an async{}.barrier
to ensure the reads and writes are synchronized. This would require that a DispatchQueue be created for every @Lazy property, but this has the side-effect of possibly creating many, many GCD queues, which isn't needed for the UIKit exception I mentioned above. This might require maintaining a pool of thread objects, assigned to each @Lazy object, but we're now adding even more complexity to what should be a simple property wrapper.
So, my suggestion is to provide two additional property wrappers to have a total of four @Lazy style objects:
An even better implementation would allow the passing of an OptionSet to supply [.constant, .queue] to the initializer, but that makes the @Lazy(flags: [], wrappedValue: { ... } var foo quite complex to write.
The text was updated successfully, but these errors were encountered:
The
@Lazy
and@LazyConstant
property wrappers do not appear to be thread safe. That is, if two threads attempt to hit the initial getter at the exact same time, they will both attempt to initialize the value. Running the initializer multiple times can have the adverse effect of executing the "expensive operation" multiple times, which is not a good thing.Note: This only would happen if the
@Lazy
objects are accessed in multiple threads. ForUIKit
initializers, everything must be done on themain
(synchronous) thread, and so we don't have this issue.The "standard" solution for such a case is creating a concurrent queue, and doing the work within the getter in a
queue.sync{}
block, and (for the non-constant@Lazy
implementation), putting the setter into anasync{}
.barrier
to ensure the reads and writes are synchronized. This would require that a
DispatchQueue
be created for every@Lazy
property, but this has the side-effect of possibly creating many, many GCD queues, which isn't needed for theUIKit
exception I mentioned above. This might require maintaining a pool of thread objects, assigned to each@Lazy
object, but we're now adding even more complexity to what should be a simple property wrapper.So, my suggestion is to provide two additional property wrappers to have a total of four
@Lazy
style objects:@Lazy
-- Current modifiable implementation@LazyConstant
-- Current constant implementation@LazyQueue
--@Lazy
+ barrier queues@LazyQueueConstant
--@LazyConstant
+ synchronous queuesAn even better implementation would allow the passing of an
OptionSet
to supply[.constant, .queue]
to the initializer, but that makes the@Lazy(flags: [], wrappedValue: { ... } var foo
quite complex to write.The text was updated successfully, but these errors were encountered: