我最近在尝试使用Swift属性包装器,并想知道是否有任何方法可以将它们组合在一起以实现更模块化的架构。例如:
@WrapperOne @WrapperTwo var foo: T
在文档中什么也找不到。唯一有关如何做到这一点的参考资料是在此 GitHub 页面上(https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md)(下面引用),似乎表明这是可能的。其他文章称它们很难组合,但没有解释如何进行操作。然而,我无法理解其中内容,如果有人能向我展示一些实现此功能的示例代码(请参见本文末尾),我将不胜感激。
理想情况下,我希望能够实现以下内容:When multiple property wrappers are provided for a given property, the wrappers are composed together to get both effects. For example, consider the composition of
DelayedMutable
andCopying
:@DelayedMutable @Copying var path: UIBezierPath
Here, we have a property for which we can delay initialization until later. When we do set a value, it will be copied via
NSCopying
's copy method. Composition is implemented by nesting later wrapper types inside earlier wrapper types, where the innermost nested type is the original property's type. For the example above, the backing storage will be of typeDelayedMutable<Copying<UIBezierPath>>
and the synthesized getter/setter for path will look through both levels of .wrappedValue:
private var _path: DelayedMutable<Copying<UIBezierPath>> = .init() var path: UIBezierPath { get { return _path.wrappedValue.wrappedValue } set { _path.wrappedValue.wrappedValue = newValue } }
Note that this design means that property wrapper composition is not commutative, because the order of the attributes affects how the nesting is performed: @DelayedMutable @Copying var path1: UIBezierPath // _path1 has type DelayedMutable> @Copying @DelayedMutable var path2: UIBezierPath // error: _path2 has ill-formed type Copying> In this case, the type checker prevents the second ordering, because
DelayedMutable
does not conform to theNSCopying
protocol. This won't always be the case: some semantically-bad compositions won't necessarily by caught by the type system. Alternatives to this approach to composition are presented in "Alternatives considered."
@propertyWrapper
struct Doubled {
var number: Int
var wrappedValue: Int {
get { (value * 2) }
set { value = Int(newValue / 2) }
}
init(wrappedValue: Int) {
self.number = wrappedValue
}
}
并且
@propertyWrapper
struct Tripled {
var number: Int
var wrappedValue: Int {
get { (value * 3) }
set { value = Int(newValue / 3) }
}
init(wrappedValue: Int) {
self.number = wrappedValue
}
}
为了实现这一目标:
@Tripled @Doubled var number = 5
我知道这个例子有点儿荒谬,但只是为了在学习新功能时更简单明了地表达。
如有需要,我们将乐意提供帮助。