在 Xcode 中使用 Markdown 生成 Swift 代碼文檔

作者:Gabriel Theodoropoulos,原文鏈接,原文日期:2016-05-12
譯者:小鐵匠Linus;校對(duì):Channe;定稿:numbbbbb

在 Xcode 7 的所有功能中,有一個(gè)很特別:它給編寫代碼文檔提供了一個(gè)更好的方法。隨著 Xcode 7 的更新,開發(fā)者可以使用 Markdown 語法書寫富文本格式的代碼文檔,而且可以結(jié)合特定的關(guān)鍵詞來指明特殊部分(如參數(shù),函數(shù)返回結(jié)果等)。作為新支持的 Markdown 文檔樣式,它具有以下幾點(diǎn)優(yōu)勢(shì):文本樣式的自定義程度更高,更加靈活,當(dāng)然也更有趣。然而,如果你仍然對(duì)原來的文本樣式感興趣的話,也可以看以前那篇教程。

對(duì)每個(gè)開發(fā)者來說,代碼文檔是開發(fā)中非常重要的一件事。即使它看上去會(huì)拖慢開發(fā)進(jìn)度,但實(shí)際上它是開發(fā)中不可或缺的一部分。我不反對(duì)給每個(gè)屬性、函數(shù)、類、結(jié)構(gòu)體等書寫正確且全面的文檔,但這并不是一件容易的事。不過,你可以通過以下幾點(diǎn)來編寫適當(dāng)?shù)奈臋n:

  • 描述各個(gè)屬性、函數(shù)和類的真正用途。此外,最好能在需要注意的地方高亮函數(shù)的具體使用情況、用例或需求。

  • 高亮函數(shù)的輸入和輸出(參數(shù)和返回值)。

  • 為了幾個(gè)月后再打開項(xiàng)目還能清晰地記得每個(gè)函數(shù)做了什么,每個(gè)屬性是為了什么。

  • 當(dāng)你把代碼共享或做成 lib 時(shí),一定要讓其他開發(fā)者能輕松地理解怎么使用你的代碼。

  • 使用工具制作具有專業(yè)外觀的使用手冊(cè)(比如:使用 Jazzy)。

你在 Xcode 里寫的代碼文檔能被預(yù)覽,也可以用以下的三種方法訪問:

  • 按住 Option/Alt 鍵點(diǎn)擊屬性名、方法名或類名等,會(huì)彈出快速預(yù)覽(Quick Look preview)。

  • 光標(biāo)移動(dòng)到屬性名、方法名或類名上,打開快速幫助(Quick Help Inspector)進(jìn)行查看。

  • 使用第三方工具可以產(chǎn)生使用手冊(cè)。比如,Jazzy 就是這樣一款工具,我們稍后會(huì)講到。通過使用它,可以在你工程的文件夾里生成一個(gè)集成了所有你寫的代碼文檔的網(wǎng)頁。

代碼文檔不是很死板的東西;它可以根據(jù)各自實(shí)體(屬性、方法、類、結(jié)構(gòu)、枚舉)的修改而改變。如果你沒有在實(shí)現(xiàn)一個(gè)新的實(shí)體時(shí)添加文檔的話,那么幾乎可以肯定,你永遠(yuǎn)不會(huì)去添加文檔了。因此,試著養(yǎng)成一個(gè)這樣的習(xí)慣:在合適的時(shí)間點(diǎn)去書寫代碼文檔,并在新的實(shí)體實(shí)現(xiàn)后能花時(shí)間去更新文檔。

Markdown 基礎(chǔ)語法

為了能更好地使用新的文檔樣式,對(duì) Markdown 語法有一個(gè)基本的認(rèn)識(shí)是很重要的。如果已經(jīng)對(duì)這部分有充分的了解的話,可以跳過這一章,直接看下一章。你可以在網(wǎng)絡(luò)上找到關(guān)于 Markdown 的很多信息,比如這里還有這里都能找到。

盡管你能找到關(guān)于 Markdown 語法的其他資源,但是我覺得至少要講一下基礎(chǔ)語法。我的目的當(dāng)然不是要提供一整個(gè) Markdown 使用指南,只是為了呈現(xiàn)特定語法的常見用法。

因此,你大概知道(可能現(xiàn)在才知道)Markdown 語法是由特殊字符來格式化文本、添加資源(鏈接和圖片)以及添加文本塊(有序或無序列表,代碼塊等)。雖然這些字符很容易記住,但是還是需要經(jīng)常上網(wǎng)查查或看看下面列出來的。有必要在這里說一下,如果你習(xí)慣了 Markdown 語法(其實(shí)很容易就做到了),然后通過使用合適的編輯器就可以生成不同格式的文檔了,例如:HTML 頁面、PDF 文檔等等。說到 HTML 頁面,Markdown 支持內(nèi)聯(lián) HTML,也就是說,你可以直接把 HTML 標(biāo)簽寫到文本里,這些標(biāo)簽都會(huì)被渲染。然而,使用 HTML 并不是 Markdown 的本質(zhì),因此我們還是回到 Markdown 自己的語法吧。

以下列出了最常用的 Markdown 語法:

  • #text#:文本標(biāo)題,相當(dāng)于 HTML 中的 <H1> 標(biāo)簽。兩個(gè) # 則對(duì)應(yīng) <H2> 標(biāo)簽,以此類推,直到 <h6> 標(biāo)簽。末尾的 # 可以省略。
  • **text**:使文本具有加粗的效果。
  • *text*:使文本具有斜體的效果。
  • * text:使文本成為一個(gè)無序列表的元素,值得注意的是,有個(gè) * 后面需要有一個(gè)空格。同樣,可以使用 + 或 - 實(shí)現(xiàn)這個(gè)的功能。
    1. text:使文本成為一個(gè)有序列表的元素。
  • [linked text](http://some-url.com):使文本成為可以點(diǎn)擊的超鏈接。
  • > text:創(chuàng)建一個(gè)塊引用。
  • 使用 4 個(gè)空格或 1 個(gè) tab 來縮進(jìn)所寫的代碼塊,等價(jià)于 HTML 中的 <pre></pre> 標(biāo)簽??梢岳^續(xù)使用 4 個(gè)空格或 1 個(gè) tab 來添加另一個(gè)縮進(jìn)。
  • 如果不想使用空格或 tab 的話,可以使用 ` 。比如, `var myProperty` 會(huì)顯示成 var myProperty
  • 另一種創(chuàng)建代碼塊的方法是添加 4 個(gè) `,并從下一行開始寫具體的代碼,最后添加 4 個(gè) ` 表示結(jié)束。
  • 反斜杠修飾 Markdown 的特殊字符就可以避免 Markdown 語法的解析了。比如, \**this\** 就不會(huì)產(chǎn)生加粗的效果。

以上這些是 Markdown 語法中比較重要的部分。雖然,Markdown 語法中還有很多額外的細(xì)節(jié)可以深究。但是,以上提供的這些已經(jīng)足夠你開始使用 Markdown 了。

如果你發(fā)現(xiàn) Markdown 還蠻有意思的話,你可以下載一個(gè)編輯器具體嘗試一下。使用編輯器,你可以實(shí)時(shí)地看到你所寫的文本在轉(zhuǎn)化成 HTML 后的效果。

關(guān)于編輯器:你可以嘗試以下這些 Markdown 編輯器:StackEdit,Typora,Macdown,FocusedUlysses

使用 Markdown

在編寫任何 Swift 中實(shí)體的文檔時(shí),有些規(guī)則是一定要遵守的。你可以為屬性(變量和常量)、方法、函數(shù)、類、結(jié)構(gòu)體、枚舉、協(xié)議、擴(kuò)展和其他代碼結(jié)構(gòu)或?qū)嶓w編寫代碼文檔。針對(duì)實(shí)體的每個(gè)文檔塊都要寫在定義或頭文件前面,且以 3 個(gè)斜線(///)或以下面的形式開頭:

/**

*/

雖然 // 也被視為注釋,但是這種語法會(huì)被 Xcode 忽略,而不產(chǎn)生對(duì)應(yīng)的代碼文檔。你可以在各種代碼塊中使用 // 來注釋,但是正如我上一句說的,這并不會(huì)產(chǎn)生對(duì)應(yīng)的代碼文檔。(譯者注:老外就是這么啰嗦,怪我咯~)

讓我們來看一個(gè)簡(jiǎn)單的例子熟悉一下 Markdown 語法吧。在下面的代碼塊中,我們?yōu)橐粋€(gè)屬性添加了一行可以產(chǎn)生代碼文檔的注釋。我建議你可以打開 Xcode 中 Playground 來試試本文中提到的所有例子。(校對(duì)注:為了保持和截圖一致,下面的注釋沒有翻譯)

/// This is an **awesome** documentation line for a really *useful* variable.
var someVar = "This is a variable"

以上寫的代碼文檔會(huì)在 Xcode 中呈現(xiàn)以下效果:

值得注意的是,單詞 “awesome” 是加粗的,而 “useful” 是斜體的。前一個(gè)是由 ** 包含,后一個(gè)則由 * 包含產(chǎn)生的。

讓我們來對(duì)函數(shù)來做另一個(gè)例子:

/**
    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 鍵點(diǎn)擊函數(shù)名,就會(huì)彈出一個(gè)快速幫助(Quick Help),如下圖:

這里我們使用了兩個(gè)新的 Markdown 元素,標(biāo)題有序列表。同時(shí),也有加粗和斜體的文本。可以看到的是,簡(jiǎn)簡(jiǎn)單單地使用了 Markdown 語法中的特殊字符就可以生成如此復(fù)雜的富文本代碼文檔。以下是快速幫助欄(Quick Help Inspector)的截圖:

下面例子中,我們?yōu)楹瘮?shù)添加一個(gè)代碼文檔中的代碼塊。值得注意的是,除了創(chuàng)建了一個(gè)代碼塊,我們還用` 標(biāo)記了內(nèi)聯(lián)函數(shù)的名稱。

/**
    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
}

以下是最終效果:

最后,讓我們來為枚舉添加代碼文檔,并在其他函數(shù)中使用。這個(gè)例子中有趣的是每個(gè)枚舉類型的代碼文檔:

/**
    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
}

現(xiàn)在,當(dāng)你使用到這個(gè)枚舉時(shí),Xcode 就會(huì)顯示之前你寫的對(duì)應(yīng)的代碼文檔:

使用關(guān)鍵字

使用 Markdown 語法只是為 Swift 代碼添加代碼文檔的一個(gè)好處。顯而易見,富文本格式很強(qiáng)大,可以展示精美的文檔內(nèi)容,但是下面要介紹另一個(gè)很厲害的關(guān)鍵字

當(dāng)你使用關(guān)鍵字時(shí),Xcode 會(huì)在渲染代碼文檔時(shí)自動(dòng)應(yīng)用對(duì)應(yīng)的文本格式(其他第三方庫也是這么做的)。關(guān)鍵字可以很方便地指出代碼結(jié)構(gòu)中常見的部分,然后區(qū)分出來。舉個(gè)例子,有很多關(guān)鍵字可以高亮方法的參數(shù)、返回值、類的作者或方法的版本。這個(gè)關(guān)鍵字的列表還挺長(zhǎng)的,然而并不是每個(gè)關(guān)鍵字都會(huì)頻繁使用,有些基本沒人用。不過大多數(shù)常見的關(guān)鍵字最好還是記在腦子里,至于其它的可以在需要時(shí)再去查。

正如上面說的,是時(shí)候通過幾個(gè)簡(jiǎn)單的例子來說明關(guān)鍵字的用法了。首先來說一下,方法或函數(shù)中接收的參數(shù)吧:

/**
    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)
}

以上代碼會(huì)顯示成這樣:

值得注意的是關(guān)鍵字前的 - 號(hào)以及與關(guān)鍵字中間的空格。后面跟隨的是具體的參數(shù)名。你必須對(duì)所有的參數(shù)都書寫對(duì)應(yīng)的描述文字。

現(xiàn)在讓我們修改一下上面的函數(shù),將這個(gè)函數(shù)返回一個(gè)字符串而不只是打印出來。為了實(shí)現(xiàn)這個(gè),我們添加了一個(gè)可以描述函數(shù)返回值的關(guān)鍵字:

/**
    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)"
}

顯示結(jié)果是這樣的:

以上這兩個(gè)關(guān)鍵字(ParameterReturns)是會(huì)用的比較頻繁的。接下來的這個(gè)函數(shù)實(shí)現(xiàn)了與上一個(gè)函數(shù)完全相反的功能,將全名拆分成姓和名:

/**
    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])
}

上面新出現(xiàn)的兩個(gè)關(guān)鍵字是 RemarkSeeAlso。通過使用 Remark,你可以高亮你想要引起讀者注意的地方。關(guān)鍵字 SeeAlso 可以引用代碼的其它部分(比如,我們這個(gè)例子中是引用了前一個(gè)函數(shù))或提供一個(gè) URL。Xcode 中的快速幫助(Quick Help)會(huì)顯示如下:

試著想象一下,上面的這個(gè)函數(shù)會(huì)分享給其他開發(fā)者使用。你肯定希望所有使用這個(gè)函數(shù)的人都能知道:參數(shù) fullname 不能是空的,且姓和名要包含在全名里,并以空格分隔,這樣函數(shù)才能正確調(diào)用。為了實(shí)現(xiàn)這個(gè),你需要使用另外兩個(gè)關(guān)鍵字 PreconditionRequires。讓我們來對(duì)應(yīng)的更新下上面的那個(gè)函數(shù):

/**
    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])
}

展望未來,你可能會(huì)樂意記下未來計(jì)劃的變更:

- Todo: Support middle name in the next version.

你甚至可以在寫代碼文檔時(shí),添加 warnings、 version、authornotes

/**
    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])
}

以下是顯示結(jié)果:

看了上面這么多例子,你應(yīng)該能理解關(guān)鍵字的詳細(xì)程度完全是由你確定的。代碼中重要的部分(比如,上面拿來舉例子的函數(shù))要有具體的代碼文檔,而次要的部分只寫基本的文檔即可。

Apple 提供了一個(gè)所有代碼文檔中可能用到的關(guān)鍵字頁面,你如果點(diǎn)了下面提到的超鏈接,你可以看到每個(gè)關(guān)鍵字采用 Markdown 語法的具體使用指南。走過路過,不要錯(cuò)過了,進(jìn)去瞧一瞧。

使用 Jazzy 產(chǎn)生代碼文檔

Jazzy 是一款可以為 Swift 和 Objective-C 代碼產(chǎn)生具有 Apple 風(fēng)格的代碼文檔工具。事實(shí)上,Jazzy 會(huì)為你創(chuàng)建一個(gè)鏈接所有代碼文檔的獨(dú)立網(wǎng)頁。它是一款命令行工具,但還是很容易使用的。

我并不打算介紹 Jazzy 的具體使用方法;只需要訪問它的 GitHub 頁面,你就能找到所有你想要的信息了,包括使用要求和安裝方法。安裝過程真的很簡(jiǎn)單,你所有要做的如下:

  1. 打開“終端”
  2. 輸入“sudo gem install jazzy”
  3. 輸入密碼
  4. 等待

為了方便你嘗試使用 Jazzy,我已經(jīng)準(zhǔn)備好了一個(gè)工程,你可以在這里下載。這個(gè)工程很簡(jiǎn)單,它就是基于之前提到的例子寫的,即組合姓和名成全名以及全名分割成姓和名。

在這個(gè)工程里,我已經(jīng)添加了和之前例子中類似的代碼文檔。即使這個(gè)工程只是為了演示,但是不管是用在什么場(chǎng)合,它不僅能支持類、方法和屬性,還能支持結(jié)構(gòu)體、枚舉、擴(kuò)展、協(xié)議等等。

假設(shè)你現(xiàn)在已經(jīng)下載了那個(gè)工程,讓我們來看看 Jazzy 到底是怎么用的。一開始,使用 cd 命令將目錄切換到工程對(duì)應(yīng)的目錄:

cd path_to_project_folder

簡(jiǎn)單地輸入 Jazzy 然后敲回車等著,然而,這樣并不能將類或其他沒有標(biāo)注為 public 的結(jié)構(gòu)寫入代碼文檔。因此,如果你想要包含所有的實(shí)體,就輸入一下:

jazzy --min-acl internal

另外,如果你用的不是 Swift 的最新版本,發(fā)現(xiàn)使用 Jazzy 后沒有效果的話,你可以通過以下命令來指定你的 Xcode 支持的 Swift 版本:

jazzy --swift-version 2.1.1 --min-acl internal

我強(qiáng)烈建議你輸入 jazzy -help 看看所有你使用 Jazzy 時(shí)可能使用到的參數(shù)。當(dāng)然,你完全可以根據(jù)自己的喜好來得到最終的結(jié)果。

以下是你生成代碼文檔頁面時(shí)會(huì)看到的:

默認(rèn)輸出的文件夾位于工程的根目錄(你也可以更改輸出路徑),叫 docs

使用 Finder 進(jìn)入到對(duì)應(yīng)路徑,在瀏覽器中打開 index.html。你立即就會(huì)發(fā)現(xiàn),默認(rèn)生成的頁面風(fēng)格和 Apple 官方文檔是非常相似的。在頁面里到處點(diǎn)擊看看,看看具體顯示的代碼文檔。接下來,就嘗試在你自己的工程中使用吧。

總結(jié)

為代碼寫文檔是一件必要且重要的事,但是開發(fā)者往往因?yàn)槿狈r(shí)間而放棄了。當(dāng)項(xiàng)目臨近交付時(shí)間或在短短時(shí)間內(nèi)還有很多 bug 要修復(fù),這就很難有機(jī)會(huì)為項(xiàng)目的每一個(gè)部分寫出合適的文檔。然而,我希望通過這篇文檔,你可以意識(shí)到代碼文檔的重要性,并試著盡量寫一下代碼文檔。你不需要寫下所有的細(xì)節(jié),但你至少要為代碼中的重要部分增加高亮,方便其他開發(fā)者或以后你自己繼續(xù)在這個(gè)代碼基礎(chǔ)上進(jìn)行修改。因此,你不要忘了還有一款專業(yè)的代碼文檔生成工具 Jazzy 可以使用。也許這可以成為你寫代碼文檔的動(dòng)力吧!

本文由 SwiftGG 翻譯組翻譯,已經(jīng)獲得作者翻譯授權(quán),最新文章請(qǐng)?jiān)L問 http://swift.gg。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容