`> 原文:Documenting Your Swift Code in Xcode Using Markdown
作者:GABRIEL THEODOROPOULOS
译者:kmyhy
在 Xcode 7 的所有新功能中,有一個最引人注目的新功能,能够讓你以更好的方式来书写代码文檔。從 Xcode 7 開始,開發者終於可以在他們的文檔中使用强大 Markdown 語法來進行富文本编辑了,Markdown 語法用一些特殊的關鍵字來描述文檔中的不同部分,比如參數、函數返回值等,從而使這些結構顯示出不同的樣式。新的 Markdown 文檔風格,最大的優點之一,是它允許對文本樣式進行更加靈活、徹底和豐富的定制。當然,如果你仍然想使用舊式的文檔風格,請參考先前的這篇教程。
對於每一個開發者而言,對代碼進行文檔化是一種極好的習慣。雖然它表面上看起來會對開發進度有一定的影響,但这本身亦应当属于開發过程的一部分。這並不意味著我們要對項目中存在的屬性、函数、類、結構或其它每一樣東西事無巨細地编到文档中去,這根本是不可能的事情。而是應該“合理”文檔化,也就是:
在 Xcode 中編寫的代碼文檔可以通過三種方法進行預覽:
代碼文檔不應當是一成不變的,它應當根據它所描述的對象(屬性、方法、類、結構、枚舉)的改變而變。有一條定律:當代碼中添加了新的對象后,如果你不立即添加新的文檔,那麼你永遠都不可能添加了。因此,我們需要養成及時編寫文檔的良好習慣,哪怕我們需要因此花費一些額外的時間,也是完全是值得的.
要想使用新的文檔風格,首先需要知道一點 Marddown 語法。如果你已經學過 Markdown 語法當然就更好了,直接跳過這部分內容即可。你可以在 web 上找到許多關於 Markdown 的介紹,如果你搜索 Markdown,立馬會得到一大堆搜索結果,例如 這裡 以及 這裡 ,這些地方你不妨先瀏覽一下。
儘管你可以在任何地方找到 Markdown 語法介紹,但我在這裡仍然要先講一下最基本的 Markdown 語法。本文的目的並不是提供一個 Markdown 語法詳細指南,因此只會簡單介紹一下 Markdown 的一些最基本的語法。
你可能已經知道(或者現在才知道)Markdown 語法是通過特殊字符來格式化文本、附加資源(鏈接和圖片)、特殊文本塊(有序列表、無序列表、代碼塊)。這些字符非常易於記憶,同時使用網絡搜索引擎或下面的這個列表,你可以隨時在腦海中強化這種記憶。如果你熟悉 Markdown 語法的話,你會發覺這裡列出的語法非常有意思(都很容易記憶),只要使用不同的編輯器,你就可以編寫出不同格式的文檔,例如 HTML、PDF 等。以 HTML 為例,Markdown 支持行內 HTML,即可以直接在文本中插入 HTML 標籤,這些標籤能正確顯示出來。當然,使用 HTML 并不是 Markdown 的主要目的,我們的注意力還是要放到它自己的語法上來。
言歸正傳,下面,讓我們來列出了 Markdown 中最常見的幾種語法:
var myProperty
。上面的內容是 Markdown 語法中最重要的基本元素。當然除此之外,Markdown 語法中還有其它的元素,而且對於上面的每一種元素,也有許多不同的用法。你可以繼續學習更多的語法,但這裡列出的內容對初學者來說已經足夠了。
如果你開始對 Markdown 產生了一點興趣,你可以下載一款免費的編輯器(在線編輯器或 Mac app),然後開始練習。如果編輯器中提供了實時預覽功能,則在你輸入的同時,文本就會被轉換成 HTML,并實時預覽到編輯文本的樣式效果。
[ecko_alert color=”gray”]編者語: 推薦幾款 Markdown 編輯器:StackEdit (在線編輯器),Typora,Macdown,Focused 和 Ulysses。[/ecko_alert]
在 Swift 中對實體對象進行文檔化時,有幾個規則必須注意。你可以文檔化屬性(變量和常量)、方法、函數、類、結構、枚舉、協議、擴展,以及任何代碼結構和實體。文檔應當寫在該實體的聲明或開頭的第一行之前,每一行都以3 個斜杠 (///) 開始,或者包含在如下所示的代碼塊之中:
/** */
兩個斜杠 (//) 也可以產生一個注釋,但它們會被 Xcode 忽略,以致它們不會被顯示成任何文檔。在各種代碼塊(例如方法體內)使用雙斜杠,但對於完整的對象注釋則使用前两者。
先來看一個簡單的 Markdown 語法的例子。下面是一個代碼片段,我們對一個屬性進行了文檔化。在代碼片段之後,是在這個屬性使用快速幫助(Quick Help)的截圖。我建議你用 Xcode 打開一個新的 Playground ,來測試這個例子。
/// This is an **awesome** documentation line for a really *useful* variable. var someVar = "This is a variable"
上述代碼將導致如下效果:
注意單詞 “awesome” 是粗體,因為它被兩個星號括住了,而單詞 “useful” 是斜體,因為它被單個星號括住。
再來看另一個例子,這次是對函數進行文檔化:
/** It calculates and returns the outcome of the division of the two parameters. ## Important Notes ## 1. Both parameters are **double** numbers. 2. For a proper result the second parameter *must be other than 0*. 3. If the second parameter is 0 then the function will return nil. */ func performDivision(number1: Double, number2: Double) -> Double! { if number2 != 0 { return number1 / number2 } else { return nil } }
如果你將上述代碼拷貝﹣粘貼到你的 Playground 文件,然後用 Option+滑鼠左鍵點擊函數名,將顯示 Quick Help 如下:
其中用到了兩個的 Markdown 元素,文字標題和有序列表。同時,在有序列表中使用了粗體和斜體。可以看到,使用 Markdown 中特有的特殊符號,能讓我們輕易地在文檔中展現豐富的文字樣式。上面的代碼在快速幫助檢視器中的顯示效果如下:
接下來,我們將在函數文檔中使用代碼塊。注意,反引號 (`) 除了能用於創建代碼塊之外,我們還會在引用函數名的時候用到反引號 (`)。
/** It doubles the value given as a parameter. ### Usage Example: ### ```` let single = 5 let double = doubleValue(single) print(double) ```` * Use the `doubleValue(_:)` function to get the double value of any number. * Only ***Int*** properties are allowed. */ func doubleValue(value: Int) -> Int { return value * 2 }
得到的效果如下:
最終,讓我們為 enum 編寫文檔,然後在另一個函數中使用這個枚舉。注意枚舉中的每一個 case 分支都添加了文檔:
/** My own alignment options. ```` case Left case Center case Right ```` */ enum AlignmentOptions { /// It aligns the text on the Left side. case Left /// It aligns the text on the Center. case Center /// It aligns the text on the Right side. case Right } func doSomething() { var alignmentOption: AlignmentOptions! alignmentOption = AlignmentOptions.Left }
當用到枚舉的每個 case 時,Xcode 都會顯示我們為對應 case 所編寫的注釋。
在 Swift 代碼中編寫文檔,我們不僅僅只有 Markdown 語法一種工具可用。富文本格式當然很酷,它能讓文檔顯得更加漂亮,但也僅僅只是如此而已。我們還可以使用另一種工具,即所謂的 關鍵字。
使用關鍵字,能夠使 Xcode 自動應用默認的格式來顯示文檔(在使用第三方庫編譯文檔時也是有效的)。關鍵字可用於表示代碼結構的不同組成部分。例如,有一些關鍵字是用于對方法參數、返回值,類的作者、函數的版本進行高亮處理的。關鍵字的數量龐大,而且也不是所有的關鍵字都那麼好用。其中一些關鍵字使用的機會並不多。但是,你需要將其中最常用的幾個關鍵字牢記於心,而剩下的關鍵字隨用隨查就可以了。
然後,讓我們來看一些使用關鍵字的例子。第一個例子是關於方法或函數參數的:
/** This is an extremely complicated method that concatenates the first and last name and produces the full name. - Parameter firstname: The first part of the full name. - Parameter lastname: The last part of the fullname. */ func createFullName(firstname: String, lastname: String) { let fullname = "\(firstname) \(lastname)" print(fullname) }
上面的代碼最終效果如下圖所示:
注意,在關鍵字前面的短橫線 (-),在橫線和關鍵字之間還有一個空格。然後是參數名,以及對該參數的描述或說明。注意,Parameter 關鍵字的個數以及後面的描述必須和方法實際參數一致。
現在,我們來對上面的函數做一點修改,返回一個人的真正全名(姓和名)而不是打印它。我們需要增加一個關鍵字,以便描述函數的返回值:
/** This is an extremely complicated method that concatenates the first and last name and produces the full name. - Parameter firstname: The first part of the full name. - Parameter lastname: The last part of the fullname. - Returns: The full name as a string value. */ func createFullName(firstname: String, lastname: String) -> String { return "\(firstname) \(lastname)" }
最終效果如:
上面用到了兩個關鍵字 (parameter 和 returns) ,這是我們用得最多的關鍵字了。但還有其它一些關鍵字。下面的這個函數是上面函數的逆操作,將一個人的全名分解成姓和名:
/** Another complicated function. - Parameter fullname: The fullname that will be broken into its parts. - Returns: A *tuple* with the first and last name. - Remark: There's a counterpart function that concatenates the first and last name into a full name. - SeeAlso: `createFullName(_:lastname:)` */ func breakFullName(fullname: String) -> (firstname: String, lastname: String) { let fullnameInPieces = fullname.componentsSeparatedByString(" ") return (fullnameInPieces[0], fullnameInPieces[1]) }
這裡出現了新的關鍵字:Remark 和 SeeAlso。前者用於高亮某些重要或特殊的內容,讓讀者一眼就能注意到這個地方(或者未來你應該對這個地方引起重視)。SeeAlso 關鍵字常用於引用代碼的另一個地方(例如,在這個例子中,我們引用到前一個函數),或者在這裡提供一個真正的 URL。 Xcode 的快速幫助會顯示為:
假設你將上述函數放到一個庫中分發給其他開發者。為了使你的工作更完善,你可能想讓函數的調用者知道兩件事情(或者使用的前置條件):fullname
參數不能為空,同時 fullname 字符串中應該包含一個空格以便將其分為兩部分,否則函數無法正確工作。這就需要用到另外兩個關鍵字,即 Precondition 和 Requires。我們需要將上述函數的文檔修改為:
/** Another complicated function. - Parameter fullname: The fullname that will be broken into its parts. - Returns: A *tuple* with the first and last name. - Remark: There's a counterpart function that concatenates the first and last name into a full name. - SeeAlso: `createFullName(_:lastname:)` - Precondition: `fullname` should not be nil. - Requires: Both first and last name should be parts of the full name, separated with a *space character*. */ func breakFullName(fullname: String) -> (firstname: String, lastname: String) { let fullnameInPieces = fullname.componentsSeparatedByString(" ") return (fullnameInPieces[0], fullnameInPieces[1]) }
接下來,你可能想在未來某個時候對函數做一些改變,并將這個計劃記錄下來以防過後就遺忘掉:
- Todo: Support middle name in the next version.
你還可以使用 warnings、 version、 author以及notes 關鍵字:
/** Another complicated function. - Parameter fullname: The fullname that will be broken into its parts. - Returns: A *tuple* with the first and last name. - Remark: There's a counterpart function that concatenates the first and last name into a full name. - SeeAlso: `createFullName(_:lastname:)` - Precondition: `fullname` should not be nil. - Requires: Both first and last name should be parts of the full name, separated with a *space character*. - Todo: Support middle name in the next version. - Warning: A wonderful **crash** will be the result of a `nil` argument. - Version: 1.1 - Author: Myself Only - Note: Too much documentation for such a small function. */ func breakFullName(fullname: String) -> (firstname: String, lastname: String) { let fullnameInPieces = fullname.componentsSeparatedByString(" ") return (fullnameInPieces[0], fullnameInPieces[1]) }
在 Quick Help 中會顯示為這個樣子:
文檔的細略程度取決於你。對於核心和至關重要的代碼,無疑需要像前面的例子一樣進行詳細的文檔化,但對於次要一些的代碼,就只需要編寫一些基本的文檔即可。
蘋果官方有一個詳盡的文檔列出了全部的關鍵字以及其它內容,你可以訪問下面的鏈接,你會看到關於 Markdown 語法的詳細用法。這篇文檔是絕對值得一看的,請點這裡。
Jazzy 是一個強大的工具,用於將 Objective-C 或 Swift 代碼中的注釋編譯成蘋果風格的幫助文檔。實際上,Jazzy 能夠創建一個能夠單獨瀏覽的網站,并將你在代碼中書寫的全部文檔都包含到其中。它是一個命令行工具,但用法真的很簡單。
我不會討論 Jazzy 的工作機制,你可以在它的 GitHub 頁上找到你想知道的一切。同時也可以看到使用它需要的條件以及它的安裝方法。整個過程其實非常簡單,你所需要做的僅僅是:
[sudo] gem install jazzy
為了便於你測試 Jazzy 的使用,我寫了一個小的 app,你可以從這裡下載。 app 其實很簡單(同時也沒有多少實際的功能),只是基於先前例子的基礎上編寫的。它能夠將一個人的姓和名組合成全名,也能將全名分解成姓和名。
在這個 app 中,我編寫了一些文檔,就像我們之前所討論的一樣。儘管這個 app 的目的只是為了演示,但你可以看到它進行了充分的文檔化,不僅對類、方法和屬性進行了文檔化,也對結構、枚舉、擴展、協議等進行了文檔化。
如果你現在已經下好了這個 app,我們就開始學習 Jazzy 的使用了。首先,在終端程序中,用 cd
命令進入項目目錄:
cd path_to_project_folder
在最簡單的情況下,你只需要輸入Jazzy
,然後坐等 Jazzy 完成它的工作即可。但是,這不會包含哪些沒有用 public 修飾的類或結構。如果你想包含所有的東西,則需要使用這個命令:
jazzy --min-acl internal
另外,如果你沒有使用 Swift 的最新版本,你不會看到 Jazzy 的輸出結果,這時你可以在參數中指定一個你使用的 Xcode 所對應的 Swift 語言版本:
jazzy --swift-version 2.1.1 --min-acl internal
我強烈建議你用 jazzy —help
命令瀏覽一下所有的 Jazzy 參數。 你會發現它有許多強大的開關 ,通過這些開關你可以隨心所欲地指定 Jazzy 的工作方式。
在創建文檔的過程中,你會在控制台中看到如下輸出,這表示文檔已經創建完畢。
默認的輸出目錄是項目根目錄下的 docs 文件夾(你可以修改它)。
用 Finder 打開 docs 目錄,在瀏覽器中打開 index.html 頁面。你會立即發現這些文檔的默認樣式和蘋果官方的文檔極其相似。隨意在頁面中點擊一些鏈接,試圖在不同頁面中跳轉,并查看頁面顯示的內容。現在,在你自己的項目中使用 Jazzy 吧!
對代碼進行文檔化是必要且重要的工作,但大部分程序員卻因為時間不夠的原因而忽視它。當項目臨近交付的最后期限時,連修復 Bug 的時間都不夠,我們確實很難為項目的每部分代碼都編寫必要的文檔。但是,我希望通過這篇文章,讓你意識到文檔的重要性,并養成盡量編寫文檔的習慣。你不必在文檔中事無巨細地記錄所有細節,而應當對重要的信息進行強調,以便提醒其它開發者,或者讓自己在一段時間后能夠輕鬆地在原代碼的基礎上繼續工作。除此之外,你還可以通過 Jazzy 將你的代碼生成專業的幫助文檔。這會大大激發我們的工作慾望。愉快地在你代碼中編寫文檔吧!
楊宏焱,男,中國大陸籍人士,CSDN 博客專家(個人博客 http://blog.csdn.net/kmyhy)。2009 年開始學習蘋果 iOS 開發,精通 O-C/Swift 和 Cocoa Touch 框架,開發有多個商店應用和企業 App。熱愛寫作,著有和翻譯有多本技術專著,包括:《企業級 iOS 應用實戰》、《iPhone & iPad 企業移動應用開發秘笈》、《iOS8 Swift 編程指南》,《寫給大忙人看的 Swift》(合作翻譯)、《iOS Swift game Development cookbook》等。