gpt4 book ai didi

swift - 如何编写 swift 属性包装器?

转载 作者:行者123 更新时间:2023-12-03 09:20:42 24 4
gpt4 key购买 nike

我最近一直在尝试使用 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 and Copying:

@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 type DelayedMutable<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 the NSCopying 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

我知道这个例子是实现属性包装器组合的一个有点愚蠢的理由,但这只是为了在学习新功能时简单起见。

任何帮助将不胜感激。

最佳答案

从 Swift 5.2 开始,嵌套属性包装器变得更加稳定,但使用起来仍然有些困难。我写了一篇文章here关于它,但诀窍是因为外包装的 wrappedValue是内包装的类型,内包装的wrappedValue是直接属性类型,您必须让包装器对这两种类型都进行操作。

我遵循的基本思想是创建一个包装器运行的协议(protocol)。然后,您也可以让其他包装器也符合该协议(protocol),以启用嵌套。

例如,在 Doubled 的情况下:

protocol Doublable {
func doubling() -> Self
func halving() -> Self
}

@propertyWrapper
struct Doubled<T: Doublable> {
var number: T
var wrappedValue: T {
get { number.doubling() }
set { number = newValue.halving() }
}
init(wrappedValue: T) {
self.number = wrappedValue
}
}

extension Int: Doublable {
func doubling() -> Int {
return self * 2
}

func halving() -> Int {
return Int(self / 2)
}
}

extension Doubled: Doublable {
func doubling() -> Self {
return Doubled(wrappedValue: self.wrappedValue)
}

func halving() -> Self {
return Doubled(wrappedValue: self.wrappedValue)
}
}

struct Test {
@Doubled @Doubled var value: Int = 10
}

var test = Test()
print(test.value) // prints 40

你可以对 Tripled 做同样的事情,使用 Tripleable 协议(protocol),等等。

但是,我应该注意,而不是嵌套 @Tripled @Doubled ,最好创建另一个包装器,如 @Multiple(6)相反:那么您将不必处理任何协议(protocol),但您将获得相同的效果。

关于swift - 如何编写 swift 属性包装器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59739242/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com