術語 「樣板」 能夠追溯到印刷媒體的早期階段。小型地區性報紙的填充量只有幾英寸,但一般缺少編寫人員來實現這一目標,所以他們中的許多人轉向大型印刷品集團,以得到穩定的內容流,能夠逐字添加到他們日報的後頁。這些故事一般在預設板上提供,這相似於用於製造鍋爐的軋製鋼板,所以得名。html
經過轉喻的過程,內容自己被稱爲「樣板」,而且該概念被用於包含合同中的標準化公式文本,形式字母,以及與本週關於NSHipster的代碼的文章最相關。git
並不是全部代碼都是迷人的。實際上,許多使一切工做的低級基礎設施都是一個模板。github
這是斯威夫特標準庫,其中包括像符號整數類型的家庭(真Int8
,Int16
,Int32
,Int64
),其實現僅在相應類型的大小而變化。數據庫
複製粘貼代碼能夠做爲一次性解決方案(假設您第一次設法正確),但它不可持續。每當您想要對這些派生實現進行更改時,您均可能會引入輕微的不一致性,致使實現隨着時間的推移而發生分歧 - 與負責地球生命變化的隨機突變不一樣。編程
語言有各類技術來處理這個問題,從C ++模板和Lisp宏到eval
C預處理器語句。json
Swift沒有宏系統,而且由於標準庫自己是用Swift編寫的,因此它沒法利用C ++元編程功能。相反,Swift維護者使用一個名爲gyb.py的Python腳本,使用一 小組 模板標籤生成源代碼。swift
GYB 是「Generate Your Boilerplate」的首字母縮寫,參考 另外一個Python工具, GYP (「生成你的項目」) Haskell包 SYB (「Scrap Your Boilerplate」) 。xcode
GYB是一個輕量級模板系統,容許您使用Python代碼進行變量替換和流控制:bash
%{ <var class="placeholder" style="box-sizing: border-box; background: rgb(248, 248, 242); display: inline-block; padding: 0px 0.25em; border: 1px solid rgb(40, 42, 54); border-radius: 5px; font-style: normal;">code</var> }
% <var class="placeholder" style="box-sizing: border-box; background: rgb(248, 248, 242); display: inline-block; padding: 0px 0.25em; border: 1px solid rgb(40, 42, 54); border-radius: 5px; font-style: normal;">code</var>: ... % end
${ <var class="placeholder" style="box-sizing: border-box; background: rgb(248, 248, 242); display: inline-block; padding: 0px 0.25em; border: 1px solid rgb(40, 42, 54); border-radius: 5px; font-style: normal;">code</var> }
全部其餘文本都保持不變。app
能夠在Codable.swift.gyb中找到GYB的一個很好的例子。在文件的頂部,基Codable
類型被分配給實例變量:
%{
codable_types = ['Bool', 'String', 'Double', 'Float',
'Int', 'Int8', 'Int16', 'Int32', 'Int64',
'UInt', 'UInt8', 'UInt16', 'UInt32', 'UInt64']
}%
複製代碼
稍後,在實現中,迭代這些類型以生成協議要求的方法聲明:Single<wbr style="box-sizing: border-box;">Value<wbr style="box-sizing: border-box;">Encoding<wbr style="box-sizing: border-box;">Container
% for type in codable_types:
mutating func encode(_ value: ${type}) throws
% end
複製代碼
評估GYB模板會產生如下聲明:
mutating func encode(_ value: Bool) throws
mutating func encode(_ value: String) throws
mutating func encode(_ value: Double) throws
mutating func encode(_ value: Float) throws
mutating func encode(_ value: Int) throws
mutating func encode(_ value: Int8) throws
mutating func encode(_ value: Int16) throws
mutating func encode(_ value: Int32) throws
mutating func encode(_ value: Int64) throws
mutating func encode(_ value: UInt) throws
mutating func encode(_ value: UInt8) throws
mutating func encode(_ value: UInt16) throws
mutating func encode(_ value: UInt32) throws
mutating func encode(_ value: UInt64) throws
複製代碼
此圖案用於在整個文件中,以產生相似的方法相似地公式化的聲明 ,和。總的來講,GYB將樣板代碼的數量減小了幾千個LOC:encode(_:for<wbr style="box-sizing: border-box;">Key:)``decode(_:for<wbr style="box-sizing: border-box;">Key:)``decode<wbr style="box-sizing: border-box;">If<wbr style="box-sizing: border-box;">Present(_:for<wbr style="box-sizing: border-box;">Key:)
$ wc -l Codable.swift.gyb
2183 Codable.swift.gyb
$ wc -l Codable.swift
5790 Codable.swift
複製代碼
有效的GYB模板可能沒法生成有效的Swift代碼。若是派生文件中發生編譯錯誤,則可能難以肯定根本緣由。
GYB不是標準Xcode工具鏈的一部分,所以您沒法找到它xcrun
。相反,您可使用Homebrew下載它:
$ brew install nshipster/formulae/gyb
複製代碼
或者,您能夠下載源代碼並使用chmod
命令生成gyb
可執行文件(macOS上的默認安裝應該可以運行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項目訪問的地方,但要將它們與源文件分開(例如,Vendor
項目根目錄下的目錄)。
在Xcode中,單擊導航器中的藍色項目文件圖標,選擇項目中的活動目標,而後導航到「Build Phases」面板。在頂部,您將看到一個+
符號,您能夠單擊該符號以添加新的構建階段。選擇「添加新運行腳本階段」,而後在源編輯器中輸入如下內容:
find . -name '*.gyb' | \
while read file; do \
./path/to/gyb --line-directive '' -o "${file%.gyb}" "$file"; \
done
複製代碼
確保在編譯源以前訂購GYB構建階段。
如今,當您構建項目時,任何具備.swift.gyb
文件擴展名的文件都由GYB評估,GYB會輸出一個.swift
與項目中其他代碼一塊兒編譯的文件。
與任何工具同樣,知道什麼時候使用它與瞭解如何同樣重要。如下是一些能夠打開工具箱並進入GYB的示例。
您是否爲一組中的元素或序列中的項複製粘貼相同的代碼?具備可變替換的for-in循環多是解決方案。
如Codable
前面的from 示例所示,您能夠在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文件能夠導入Python包同樣json
,xml
和csv
,這樣你就能夠分析幾乎任何類型的可能會遇到的文件:
%{ import csv }
% with open('path/to/file.csv') as file:
% for row in csv.DictReader(file):
複製代碼
若是您但願看到這一點,請查看 Currencies.swift.gyb ,它爲ISO 4217規範定義的每種貨幣生成Swift枚舉 。
經過將數據下載到能夠檢入源代碼管理的文件而不是在GYB文件中進行HTTP請求或數據庫查詢,可使編譯保持快速和肯定性。
代碼生成使您的代碼與相關標準保持同步變得微不足道。只需更新數據文件並從新運行GYB便可。
斯威夫特已經作了不少與另外的編譯器合成的最近削減樣板 Encodable
和Decodable
4.0, Equatable
和Hashable
4.1,和 4.2。咱們但願這種勢頭可以在將來的語言更新中獲得體現。Case<wbr style="box-sizing: border-box;">Iterable
與此同時,對於其餘一切,GYB是代碼生成的有用工具。
社區中另外一個很棒的工具是 Sourcery,它容許你在Swift(經過Stencil)而不是Python中編寫模板。
「不要重複本身」多是編程的一種美德,但有時你必須說幾回才能使事情發揮做用。當你這樣作時,你會感謝有一個像GYB這樣的工具來爲你說。