Property Wrappers
petrpavlik opened this issue · comments
Are there plans to start making use of property wrappers? I think that it could improve the syntax in certain areas, especially Property
, so I quickly put together this swift package as a proof of concept https://github.com/petrpavlik/PropertyPropertyWrapper.
It shouldn't be hard to introduced. MutableProperty
, Property
and Atomic
can all be annotated as a property delegate.
This sounds like a nice addition to have. However, I'm concerned that adding the set
accessors without thinking about the overall design might be leading to trouble.
Specifically, the implementation of Atomic<Value>
found in petrpavlik/PropertyWrapperWrapper apparently has the shortcoming that read-and-write modifications aren't done without releasing the lock in between. AFAICT (although it's hard to reproduce) that causes a data race in really straightforward looking pieces of code such as:
@Atomic var counter: Int = 0
let queue = DispatchQueue.global()
let group = DispatchGroup()
queue.async(group: group) {
counter += 1 // <- not really atomic
}
queue.async(group: group) {
counter += 2 // <- not really atomic
}
group.notify(queue: queue) {
print(counter) // depending on locking order, might print 1 or 2 or 3
}
I find this especially problematic in the case of Atomic
which has atomic in its name already.
I believe this could be fixed either:
-
by using the unofficial
_modify
accessor (probably a no-go until it becomes official), or -
by hiding
set
altogether and directing writes to be done in terms of the modification interfaces ofAtomic
andMutableProperty
type exposed through theprojectedValue
, something like:$counter.modify { $0 += 1 }
Edit: Oh, I realise this topic has been discussed in #731 (comment) too.
Spent a bit more time investigating this. For at least Atomic<Value>
, turning the type into a class-backed struct
with _modify
accessors seems to me like the sweet spot.
E.g. given this example,
final class Demo {
@Atomic var aaa: Int = 0
@Atomic private(set) bbb: Int = 10
@Atomic fileprivate(set) ccc: Int = 100
func method() { ... }
}
let demo = Demo()
demo.aaa
,demo.aaa = 1
, anddemo.aaa += 2
can be accessed freely (internal
ly),- the accessors
demo.$aaa.withValue { ... }
,demo.$aaa.swap(3)
, anddemo.$aaa.modify { ... }
are equally accessible;
- the accessors
demo.aaa += 2
only locks once during the read-and-write operation,demo.bbb
can be read by anyone but only modified byDemo.method
(where of course the always-private
generated_bbb
is visible too),demo.$bbb.withValue { ... }
is accessible wheneverdemo.bbb
is, and- the mutable interface
demo.ccc
as well asdemo.$ccc
are visible within that file.
A quick implementation which seemed to work, with minor changes to other ReactiveSwift code, is found in pyrtsa@9de23ce.
I'm assuming a similar treatment could be done to Property
and MutableProperty
too, but turning those into struct
s would be an even more breaking change, although something to that end was discussed in #19.