不能在标有 @Observable 的类上使用属性包装器

问题描述 投票:0回答:1

我有一个类似于以下的课程:

final class Person {
    @Resettable var name = "John Doe"
    
    func revertName() {
        name = $name
    }
}

@propertyWrapper
struct Resettable<T> {
    var wrappedValue: T
    let projectedValue: T
    
    init(wrappedValue: T) {
        self.wrappedValue = wrappedValue
        self.projectedValue = wrappedValue
    }
}

但是,当我用新的

Person
宏注释
@Observable
时,出现以下错误:

属性包装器不能应用于计算属性

我假设发生这种情况是因为

@Observable
宏为类中的所有属性添加了 getter 和 setter。

有什么方法可以同时使用我的

@Resettable
属性包装器和
@Observable
宏吗?

上下文:就我而言,它是

class GameScene: SKScene
,我使用
@Resettable
来重置诸如
var runningSpeed
之类的东西。游戏场景必须用
@Observable
标记,因为
GameScene
是使用名为
GameSettingsView
的 SwiftUI 视图进行修改的。该视图使用
@Bindable
@Observable
来修改游戏场景。

swift macros observable property-wrapper
1个回答
1
投票

你是对的 -

@Observable
将存储的属性转换为计算属性,因此你不能在它们上放置属性包装器。

您可以尝试将属性包装器也变成宏。对于你的

Resettable
来说,这相当简单。您只需生成一个新属性,以
$
为前缀,并具有相同的初始化程序。

我写的一个简单的实现(我不太擅长使用SwiftSyntax,所以请原谅)

public enum Resettable: PeerMacro {
    public static func expansion(of node: AttributeSyntax, providingPeersOf declaration: some DeclSyntaxProtocol, in context: some MacroExpansionContext) throws -> [DeclSyntax] {
        // only apply to vars
        guard let varDecl = declaration.as(VariableDeclSyntax.self),
            varDecl.bindingSpecifier.text == "var" else {
            return []
        }
        return varDecl.bindings.filter {
            // apply only to those with initialisers
            $0.initializer != nil && 
            // and also the name of the variable does not start with underscore
            !$0.pattern.as(IdentifierPatternSyntax.self)!.identifier
                .text.starts(with: "_") 
        }.map {
            return "let $\($0.pattern) = \($0.initializer!.value)" as DeclSyntax
        }
    }
}

@main
struct ResettablePlugin: CompilerPlugin {
    let providingMacros: [Macro.Type] = [Resettable.self]
}

// ...

@attached(peer, names: prefixed(`$`))
public macro Resettable() = #externalMacro(module: "SomeModule", type: "Resettable")

备注:

  • 我必须过滤掉名称以下划线开头的变量,因为
    @Observation
    显然会生成一个
    @Resettable var _name
    ,而我们不想添加
    $_name
  • 初始化程序将运行两次,因此请确保它没有副作用。
© www.soinside.com 2019 - 2024. All rights reserved.