前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >针对属性的条件编译优化

针对属性的条件编译优化

原创
作者头像
DerekYuYi
修改2022-11-09 14:16:54
9060
修改2022-11-09 14:16:54
举报
文章被收录于专栏:Swift-开源分析

SE-0367, Swift 5.8 中实现

现有问题

随着时间的推移,Swift 引入了许多新的属性,用来在源代码中传递额外信息。现有代码可以利用新的构造来改进,引入新功能,提供新的编译检查,更好的性能等等。但是,现有代码引入新属性意味着不能在旧的编译器上使用。自然而然你会想到用条件编译来解决该问题。例如可以使用#if 检查编译器版本,查看是否可以使用@preconcurrency属性:

代码语言:Swift
复制
#if compiler(>=5.6)
@preconcurrency protocol P: Sendable {
  func f()
  func g()
}
#else
protocol P: Sendable {
  func f()
  func g()
}
#endif

有没有发现这段代码有几个缺点?首先,有两段重复代码,因为P协议被定义2次;其次,Swift 5.6 是第一个包含@preconcurrency属性的编译器,但这不是由编译器自动记录的:该属性可能是由编译器标志启用的,也可能是在 Swift 5.7 开发到一半弃用的,因此检查不正确。而且,一些属性是否可用不是依赖编译器,而是平台和配置标志。例如,@objc仅在 Swift 运行时编译用于和 Objective-C 交互时可用。

尽管上述这些都是孤立的小问题,但它们让在现有代码中采用新属性比实际情况更困难。

提议方案

为了在现有代码中更容易使用新属性,本篇提议 2 个更改:

  • 无论属性声明在哪里,允许#if检查出现在声明属性的前面,无需再复制属性声明,仅表示为了采用新属性。
代码语言:Swift
复制
#if hasAttribute(preconcurrency)
@preconcurrency
#endif
protocol P: Sendable {
  func f()
  func g()
}
  • 新增条件命令hasAttribute(AttributeName),如果当前语言环境支持AttributeName属性,则返回true, 反之则false。 如果把这两点运用到上面的例子,是不是更加简洁,可读性更强。

设计细节

语法改变

当前属性列表生成语法为:

代码语言:Swift
复制
attributes → attribute attributes[opt]

将通过添加条件属性来生成:

代码语言:Swift
复制
attributes → conditional-compilation-attributes attributes[opt]

conditional-compilation-attributes → if-directive-attributes elseif-directive-attributes[opt] else-directive-attributes[opt] endif-directive
if-directive-attributes → if-directive compilation-condition attributes[opt]
elseif-directive-attributes → elseif-directive-attributes elseif-directive-attributes[opt]
elseif-directive-attributes → elseif-directive compilation-condition attributes[opt]
else-directive-attributes → else-directive attributes[opt]

在属性列表中,可以存在一个条件子句#if...#endif 来包含另外一个属性列表,嵌套使用。

hasAttribute的校验属性对象是:作为当前语言的一部分

许多语言特性,包括属性包装器(property wrapper), 结果构建器(result builder),还有 global actors, 等等,都引入了自定义属性的方式来实现。例如, 类型 MyWrapper 使用属性@propertyWrapper标记,该类型已经实现@propertyWrapper属性的要求,那么该类型可以在其他地方通过@MyWrapper用法来使用它。虽然启用该功能的内置属性(也可以说是原始属性)可以被hasAttribute识别,比如hasAttribute(propertyWrapper) 结果会被判断为true,但是基于原始属性的自定义属性不会被识别。也就是说hasAttribute(MyWrapper)的结果是false, 不是true, 因为MyWrapper是自定义属性。

解析编译器不接受的条件编译 if 分支

由于支持自定义属性,属性具有非常通用的语法,对于我们在 Swift 引入任何新的特性来说,都足够了。

代码语言:Swift
复制
attribute → @ attribute-name attribute-argument-clause[opt]
attribute-name → identifier
attribute-argument-clause → ( balanced-tokens[opt] )

因此,基于#if hasAttribute(UnknownAttributeName)的条件编译分支,仍然能在现有的编译器上解析,即使该条件不能用于声明上,因为虽然走进了对应的 if 分支,但是编译器有可能无法识别该内容。

代码语言:Swift
复制
#if hasAttribute(UnknownAttributeName)
@@UnknownAttributeName(something we do not understand) // okay, we parse this but don't reject it
#endif
func f()

总结

  1. Swift 5.8 中使用#if hasAttribute(AttributeName)来检查当前语言环境下支持的属性关键字,取代冗长的版本判断,去除对声明的重复定义。
  2. 新属性在旧的编译器声明,编译检查不会报错。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 现有问题
  • 提议方案
  • 设计细节
    • 语法改变
      • hasAttribute的校验属性对象是:作为当前语言的一部分
        • 解析编译器不接受的条件编译 if 分支
        • 总结
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档