我想创建一个 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什么都不做。
将宏应用于闭包被列为函数体宏的“未来方向”之一,但目前尚未实现。
解决方法是创建一个扩展为闭包的表达式宏,以便您可以执行以下操作:
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)
}
}