【译】Swift GYB

203 阅读5分钟

原文链接 Swift GYB

*“样板”*一词可以追溯到印刷媒体的早期。小型地区性报纸需要填满几英寸的专栏,但通常缺乏写作人员来实现这一目标,因此许多报纸转向大型印刷辛迪加,以获得稳定的内容流,这些内容可以一字不差地添加到日报的后页。这些故事通常会在预先设置的盘子上提供,这就像用来制造锅炉的钢板,因此得名。

通过一个转喻过程,内容本身被称为“样板”,这个概念被用来包含合同、格式信函中的标准化公式化文本,以及与本周关于NS Hipster的文章最相关的代码。

并不是所有的代码都有魅力。事实上,许多让一切正常工作的低级基础设施都是一大堆样板。

Swift标准库就是如此,它包括有符号整数(Int8、Int16、Int32、Int64)等类型的系列,这些类型的实现仅因各自类型的大小而异。

复制粘贴代码可能是一次性的解决方案(假设你第一次就成功了),但它是不可持续的。每次你想对这些派生实现进行更改,你都有可能引入轻微的不一致,导致实现随着时间的推移而出现分歧——这与导致地球上生命变化的随机突变没有什么不同。

语言有各种各样的技术来处理这个问题,从C++模板和Lisp宏到评估和C预处理器语句。

Swift没有宏系统,因为标准库本身是用Swift编写的,所以它不能利用C++元编程功能。相反,Swift维护者使用名为**gyb.py的Python**脚本,使用一小组模板标签生成源代码。

GYB如何工作

GYB是一个轻量级模板系统,允许您使用Python代码进行变量替换和流控制:

  • 序列%{code}计算一个Python代码块

  • 序列%code: ... % 端管理控制流

  • 序列${code}替换表达式的结果

所有其他文本通过不变。

GYB的一个很好的例子可以在**Codable.swift.gyb.**在文件的顶部,基本的可编码类型被分配给一个实例变量:

%{codable_types = ['Bool', 'String', 'Double', 'Float',                 'Int', 'Int8', 'Int16', 'Int32', 'Int64',                 'UInt', 'UInt8', 'UInt16', 'UInt32', 'UInt64']}%

稍后,在单值编码容器的实现中,这些类型被迭代以生成协议需求的方法声明:

% for type in codable_types:  mutating func encode(_ value: ${type}) throws% end

评估GYB模板会得到以下声明:

mutating func encode(_ value: Bool) throwsmutating func encode(_ value: String) throwsmutating func encode(_ value: Double) throwsmutating func encode(_ value: Float) throwsmutating func encode(_ value: Int) throwsmutating func encode(_ value: Int8) throwsmutating func encode(_ value: Int16) throwsmutating func encode(_ value: Int32) throwsmutating func encode(_ value: Int64) throwsmutating func encode(_ value: UInt) throwsmutating func encode(_ value: UInt8) throwsmutating func encode(_ value: UInt16) throwsmutating func encode(_ value: UInt32) throwsmutating func encode(_ value: UInt64) throws

此模式用于在整个文件中为诸如encode(: for Key:)、decode(: for Key:)和decode IF Present(_: for Key:)等方法生成类似的公式化声明。GYB总共减少了几千个样板代码:

$ wc -l Codable.swift.gyb2183 Codable.swift.gyb$ wc -l Codable.swift5790 Codable.swift

安装GYB

GYB不是标准Xcode工具链的一部分,所以你不会在xc run中找到它。相反,你可以**使用**Homebrew下载它:

$ brew install nshipster/formulae/gyb

或者,您可以下载源代码并使用chmod命令使gyb可执行(mac OS上Python的默认安装应该能够运行gyb):

$ wget https://github.com/apple/swift/raw/master/utils/gyb$ wget https://github.com/apple/swift/raw/master/utils/gyb.py$ chmod +x gyb

如果你走这条路,一定要把它们移到你的Xcode项目可以访问的地方,但是要把它们和你的源文件分开(例如,你项目根目录下的供应商目录)。

在Xcode中使用GYB

在Xcode中,单击导航器中的蓝色项目文件图标,选择项目中的活动目标,并导航到“构建阶段”面板。在顶部,您将看到一个+符号,您可以单击该符号来添加新的构建阶段。选择“添加新运行脚本阶段”,并在源编辑器中输入以下内容:

find . -name '*.gyb' |                                               \    while read file; do                                              \        ./path/to/gyb --line-directive '' -o "${file%.gyb}" "$file"; \    done

现在,当您构建项目时,任何具有.swift.gyb文件扩展名的文件都会由GYB评估,GYB会输出一个。斯威夫特文件,该文件与项目中的其余代码一起编译。

何时使用GYB

和任何工具一样,知道何时使用它和知道如何使用一样重要。以下是一些例子,说明您何时可以打开工具箱并使用GYB。

生成公式代码

您是将相同的代码复制粘贴到集合中的元素还是序列中的项?解决方案可能是使用变量替换的for-in循环。

就像前面的可编程示例中看到的那样,您可以在GYB模板文件的顶部声明一个集合,然后迭代该集合以声明类型、属性或方法:

%{    abilities = ['strength', 'dexterity', 'constitution',                 'intelligence', 'wisdom', 'charisma']}%class Character {  var name: String% for ability in abilities:  var ${ability}: Int% end}

使用GYB进行评估会产生以下Swift代码:

class Character {  var name: String  var strength: Int  var dexterity: Int  var constitution: Int  var intelligence: Int  var wisdom: Int  var charisma: Int}

代码中的大量重复是一种味道,可能表明有更好的方法来完成你的任务。协议扩展和泛型等内置语言功能可以消除大量代码重复,所以要注意使用这些功能,而不是用GYB强制执行。

从数据生成代码

你在根据数据源编写代码吗?试着将GYB融入你的开发中!

GYB文件可以导入json、xml和csv等Python包,因此您可以解析几乎任何类型的文件:

%{ import csv }% with open('path/to/file.csv') as file:    % for row in csv.DictReader(file):

如果您想看到这一点,请查看**Currencies.swift.gyb,它为ISO 4217**规范定义的每种货币生成Swift枚举。

代码生成使得保持代码与相关标准同步变得微不足道。只需更新数据文件并重新运行GYB。

Swift最近做了很多工作来减少样板文件,增加了编译器合成4.0中的可编码和可解码,4.1中的可等式和可哈斯布尔,4.2中的Cas eI terable。我们希望这种势头在未来的语言更新中得到延续。

与此同时,对于其他一切,GYB是一个有用的代码生成工具。

“不要重复自己”可能是编程中的一个优点,但有时你必须说几遍才能让事情运转起来。当你这样做的时候,你会感谢有一个像GYB这样的工具为你说出来。