如何在 Swift 中创建一个不返回值而只是替换代码执行的宏?

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

我想创建一个 Swift 宏,它不返回要插入某处的值;我想要一个可以简单地替换代码的宏。

具体来说,我想避免每次在闭包中捕获 self 时不断重复以下行:

guard let self = self else { assertionFailure(); return }

并使用类似:

let closure = { [weak self] in
            #weakSelf
            // ... actual code
          }

理想情况下,我希望能够将一个值传递给应该从插入它的函数返回的宏,但这是下一步。

我做了什么: 我使用 Swift Package Manager 创建了一个 Swift 宏。 我将在下面留下代码。

问题:这个宏是否可能,如果可以,我做错了什么?

实际代码:

@freestanding(expression)
public macro weakSelf() = #externalMacro(
    module: "MyMacroMacros",
    type: "SelfGuardMacro"
)

@main
struct MyMacroPlugin: CompilerPlugin {
    let providingMacros: [Macro.Type] = [
        SelfGuardMacro.self,
    ]
}

public struct SelfGuardMacro: ExpressionMacro {
    public static func expansion(
        of node: some SwiftSyntax.FreestandingMacroExpansionSyntax,
        in context: some SwiftSyntaxMacros.MacroExpansionContext
    ) throws -> ExprSyntax {
        return ExprSyntax(stringLiteral: """
        guard let self = self else {
            assertionFailure()
            return
        }
        """)
    }
}

使用:

class MyClass {
    var closure: (() -> Void)!
    
    func function() {
        self.closure = { [weak self] in
            #weakSelf
            self.printString()            
        }
    }
    
    func printString() {
        print("printing")
    }
}

在 self.printString() 上我收到错误:

可选类型“MyClass?”的值必须解包才能引用包装基类型“MyClass”的成员“printString”

就像#weakSelf什么都不做。

swift macros closures swift-package-manager retain-cycle
1个回答
0
投票

将宏应用于闭包被列为函数体宏的“未来方向”之一,但目前尚未实现。

解决方法是创建一个扩展为闭包的表达式宏,以便您可以执行以下操作:

doWork(completionHandler: #WeakSelfClosure { result in
    // ...
})

这将扩展到

doWork(completionHandler: { [weak self] result in
    guard let self else {
        return
    }
    // ...
})

这是一个示例实现:

// declaration
@freestanding(expression)
public macro WeakSelfClosure<each P>(
    closure: (repeat each P) -> Void
) -> (repeat each P) -> Void = #externalMacro(module: "...", type: "WeakSelfClosure")


// implementation
enum WeakSelfClosure: ExpressionMacro {
    static func expansion(of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext) throws -> ExprSyntax {
        guard var closure = node.trailingClosure else {
            throw SomeError()
        }
        var signature = closure.signature
        let weakSelfCapture = ClosureCaptureSyntax(
            specifier: .init(specifier: "weak"),
            expression: DeclReferenceExprSyntax(baseName: "self")
        )
        if var signature = closure.signature {
            signature.capture = .init(items: .init {
                for capture in signature.capture?.items ?? [] {
                    capture
                }
                weakSelfCapture
            })
            closure.signature = signature
        } else {
            closure.signature = ClosureSignatureSyntax(capture: .init(items: [weakSelfCapture]))
        }
        closure.statements = .init {
            "guard let self else { return }"
            for stmt in closure.statements {
                stmt
            }
        }
        return ExprSyntax(closure)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.