Swift 程式語言

如何使用 Markdown 為 Swift 程式碼加入註解

如何使用 Markdown 為 Swift 程式碼加入註解
如何使用 Markdown 為 Swift 程式碼加入註解
In: Swift 程式語言

Markdown 語法用一些特殊的關鍵字來描述文檔中的不同部分,比如參數、函數返回值等,從而使這些結構顯示出不同的樣式。新的 Markdown 文檔風格,最大的優點之一,是它允許對文本樣式進行更加靈活、徹底和豐富的定制。當然,如果你仍然想使用舊式的文檔風格,請參考先前的這篇教程

對於每一個開發者而言,為程式碼加入註解是一種極好的習慣。雖然它表面上看起來會對開發進度有一定的影響,但這本身亦應當屬於開發過程的一部分。這並不意味著我們要對項目中存在的屬性、函数、類、結構或其它每一樣東西事無巨細地編到文檔中去,這根本是不可能的事情。而是應該「合理」說明解釋,也就是:

  • 描述類、方法和屬性的用途,粗略程度要適當。而且最好對調用方法時應當注意的前提條件、事項或限制做重點說明。
  • 高亮標出方法的輸入/輸出(參數及返回值)。
  • 當你事隔幾個月再次閱讀項目程式碼時,程式碼中每一個方法的用途和每一個屬性的意義你都能輕鬆憶起。
  • 當你分享程式碼或庫時,別的開發者能夠很容易就明白這些程式碼要如何使用。
  • 通過某些工具(例如 Jazzy),我們就能生成專業的幫助文檔。

在 Xcode 中編寫的程式碼文檔可以通過三種方法進行預覽:

  • 在屬性名、方法名或類名上點擊 Option/Alt+鼠標左鍵。你就可以在這些名字的右側或者下方看到屬性、方法或類的快速概覽(Quick Look Preview)。
  • 把鼠標放到屬性名、方法名或類名上,則會打開快速幫助檢視器(Quick Help Inspector)。
  • 有一些第三方工具能夠將程式碼中的文檔製作成標準幫助文檔,例如 Jazzy,我們稍後會介紹它。通過 Jazzy,你的文檔將會以網頁的形式編譯,同時所有的網頁會生成到項目文件夾下的一個子文件中,這些網頁構成了一個可獨立運行的 web 網站。

程式碼文檔不應當是一成不變的,它應當根據它所描述的對象(屬性、方法、類、結構、enum)的改變而變。有一條定律:當程式碼中添加了新的對象後,如果你不立即添加新的文檔,那麼你永遠都不可能添加了。因此,我們需要養成及時編寫文檔的良好習慣,哪怕我們需要因此花費一些額外的時間,也是完全是值得的.

Markdown 語法基礎

要想使用新的文檔風格,首先需要知道一點 Markdown 語法。如果你已經學過 Markdown 語法當然就更好了,直接跳過這部分內容即可。你可以在 web 上找到許多關於 Markdown 的介紹,如果你搜索 Markdown,立馬會得到一大堆搜索結果,例如 這裡 以及 這裡 ,這些地方你不妨先瀏覽一下。

儘管你可以在任何地方找到 Markdown 語法介紹,但我在這裡仍然要先講一下最基本的 Markdown 語法。本文的目的並不是提供一個 Markdown 語法詳細指南,因此只會簡單介紹一下 Markdown 的一些最基本的語法。

你可能已經知道(或者現在才知道)Markdown 語法是通過特殊字符來格式化文本、附加資源(鏈接和圖片)、特殊文本塊(有序列表、無序列表、程式碼塊)。這些字符非常易於記憶,同時使用網絡搜索引擎或下面的這個列表,你可以隨時在腦海中強化這種記憶。如果你熟悉 Markdown 語法的話,你會發覺這裡列出的語法非常有意思(都很容易記憶),只要使用不同的編輯器,你就可以編寫出不同格式的文檔,例如 HTML、PDF 等。以 HTML 為例,Markdown 支持行內 HTML,即可以直接在文本中插入 HTML 標籤,這些標籤能正確顯示出來。當然,使用 HTML 并不是 Markdown 的主要目的,我們的注意力還是要放到它自己的語法上來。

言歸正傳,下面,讓我們來列出了 Markdown 中最常見的幾種語法:

  • #text#: 文字標題,等於 HTML 中的 < H1>。而連續兩個 # 則表示 < H2>,以此類推,直到 < H6>。 text 末尾的一個或多個 # 可以省略。
  • **text**: 星號之間的文字將以加粗
  • *text*: 星號之間的文字將以斜體顯示。
  • * text: 創建一個無序列表,即 項目符號 (注意在星號後面有一個空格)。此外,還可以用 + 和 ﹣ 號取代星號。在星號(或者其它兩個等義字符)後面最多可使用 3 個空格表示列表項的縮進。
  • 1. text: 創建一個有序列表(編號列表)。
  • [鏈接文字](http://some-url.com): 將鏈接文字轉變成超級鏈接。
  • > text: 創建引用。
  • 用四個 (4) 空格或一個 tab 表示一個程式碼塊。等於 HTML 中的 <pre></pre>標籤。每縮進一級就增加 4 個空格或一個 tab 字符。
  • 如果不想將空格和 tab 字符搞混,可以將程式碼塊用 “`” (反引號)括住。例如 `var myProperty` 會轉換成: var myProperty
  • 另一種表示程式碼塊的方式是使用 4 個反引號(“`”),然後在下一行書寫程式碼。
  • 反斜線號用於轉義,以避免某個字符被當成是 Markdown 語法的一部分。例如,/**this/** 的結果是文字不會被加粗顯示。

上面的內容是 Markdown 語法中最重要的基本元素。當然除此之外,Markdown 語法中還有其它的元素,而且對於上面的每一種元素,也有許多不同的用法。你可以繼續學習更多的語法,但這裡列出的內容對初學者來說已經足夠了。

如果你開始對 Markdown 產生了一點興趣,你可以下載一款免費的編輯器(在線編輯器或 Mac app),然後開始練習。如果編輯器中提供了實時預覽功能,則在你輸入的同時,文本就會被轉換成 HTML,并實時預覽到編輯文本的樣式效果。

編者註: 推薦幾款 Markdown 編輯器:StackEdit (在線編輯器)TyporaMacdownFocusedUlysses

使用 Markdown

在 Swift 中對實體對象進行文檔化時,有幾個規則必須注意。你可以文檔化屬性(變量和常量)、方法、函數、類、結構、enum、協議、擴展,以及任何程式碼結構和實體。文檔應當寫在該實體的聲明或開頭的第一行之前,每一行都以3 個斜線號 (///) 開始,或者包含在如下所示的程式碼塊之中:

/**
*/

兩個斜線號 (//) 也可以產生一個注釋,但它們會被 Xcode 忽略,以致它們不會被顯示成任何文檔。在各種程式碼塊(例如方法體內)使用雙斜線號,但對於完整的對象注釋則使用前两者。

先來看一個簡單的 Markdown 語法的例子。下面是一個程式碼片段,我們對一個屬性進行了文檔化。在程式碼片段之後,是在這個屬性使用快速幫助(Quick Help)的截圖。我建議你用 Xcode 打開一個新的 Playground ,來測試這個例子。

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

上述程式碼將導致如下效果:

Quick Help in Xcode

注意單詞 “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 如下:

t52_2_quickhelp2

其中用到了兩個的 Markdown 元素,文字標題有序列表。同時,在有序列表中使用了粗體和斜體。可以看到,使用 Markdown 中特有的特殊符號,能讓我們輕易地在文檔中展現豐富的文字樣式。上面的程式碼在快速幫助檢視器中的顯示效果如下:

Help Inspector

接下來,我們將在函數文檔中使用程式碼塊。注意,反引號 (`) 除了能用於創建程式碼塊之外,我們還會在引用函數名的時候用到反引號 (`)。

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

得到的效果如下:

t52_4_quickhelp3

最終,讓我們為 enum 編寫文檔,然後在另一個函數中使用這個enum。注意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
}

當用到enum的每個 case 時,Xcode 都會顯示我們為對應 case 所編寫的注釋。

t52_5_quickhelp4

使用關鍵字

在 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)
}

上面的程式碼最終效果如下圖所示:

t52_6_keywords1

注意,在關鍵字前面的短橫線 (-),在橫線和關鍵字之間還有一個空格。然後是參數名,以及對該參數的描述或說明。注意,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)"
}

最終效果如:

t52_7_keywords2

上面用到了兩個關鍵字 (parameterreturns) ,這是我們用得最多的關鍵字了。但還有其它一些關鍵字。下面的這個函數是上面函數的逆操作,將一個人的全名分解成姓和名:

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

這裡出現了新的關鍵字:RemarkSeeAlso。前者用於高亮某些重要或特殊的內容,讓讀者一眼就能注意到這個地方(或者未來你應該對這個地方引起重視)。SeeAlso 關鍵字常用於引用程式碼的另一個地方(例如,在這個例子中,我們引用到前一個函數),或者在這裡提供一個真正的 URL。 Xcode 的快速幫助會顯示為:

t52_8_keywords3

假設你將上述函數放到一個庫中分發給其他開發者。為了使你的工作更完善,你可能想讓函數的調用者知道兩件事情(或者使用的前置條件):fullname 參數不能為空,同時 fullname 字符串中應該包含一個空格以便將其分為兩部分,否則函數無法正確工作。這就需要用到另外兩個關鍵字,即 PreconditionRequires。我們需要將上述函數的文檔修改為:

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

t52_9_keywords4

接下來,你可能想在未來某個時候對函數做一些改變,并將這個計劃記錄下來以防過後就遺忘掉:

- Todo: Support middle name in the next version.

你還可以使用 warningsversionauthor以及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 中會顯示為這個樣子:

t52_10_keywords5

文檔的細略程度取決於你。對於核心和至關重要的程式碼,無疑需要像前面的例子一樣進行詳細的文檔化,但對於次要一些的程式碼,就只需要編寫一些基本的文檔即可。

蘋果官方有一個詳盡的文檔列出了全部的關鍵字以及其它內容,你可以訪問下面的鏈接,你會看到關於 Markdown 語法的詳細用法。這篇文檔是絕對值得一看的,請點這裡

用 Jazzy 編譯文檔頁面

Jazzy 是一個強大的工具,用於將 Objective-C 或 Swift 程式碼中的注釋編譯成蘋果風格的幫助文檔。實際上,Jazzy 能夠創建一個能夠單獨瀏覽的網站,并將你在程式碼中書寫的全部文檔都包含到其中。它是一個命令行工具,但用法真的很簡單。

我不會討論 Jazzy 的工作機制,你可以在它的 GitHub 頁上找到你想知道的一切。同時也可以看到使用它需要的條件以及它的安裝方法。整個過程其實非常簡單,你所需要做的僅僅是:

  1. 打開終端程序
  2. 輸入: [sudo] gem install jazzy
  3. 輸入你的密碼
  4. 等待…

為了便於你測試 Jazzy 的使用,我寫了一個小的 app,你可以從這裡下載。 app 其實很簡單(同時也沒有多少實際的功能),只是基於先前例子的基礎上編寫的。它能夠將一個人的姓和名組合成全名,也能將全名分解成姓和名。

t52_11_app_sample

在這個 app 中,我編寫了一些文檔,就像我們之前所討論的一樣。儘管這個 app 的目的只是為了演示,但你可以看到它進行了充分的文檔化,不僅對類、方法和屬性進行了文檔化,也對結構、enum、擴展、協議等進行了文檔化。

如果你現在已經下好了這個 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 的工作方式。

在創建文檔的過程中,你會在控制台中看到如下輸出,這表示文檔已經創建完畢。

t52_13_terminal_jazzy

默認的輸出目錄是項目根目錄下的 docs 文件夾(你可以修改它)。

用 Finder 打開 docs 目錄,在瀏覽器中打開 index.html 頁面。你會立即發現這些文檔的默認樣式和蘋果官方的文檔極其相似。隨意在頁面中點擊一些鏈接,試圖在不同頁面中跳轉,并查看頁面顯示的內容。現在,在你自己的項目中使用 Jazzy 吧!

t52_15_jazzy_results

總結

對程式碼進行文檔化是必要且重要的工作,但大部分程序員卻因為時間不夠的原因而忽視它。當項目臨近交付的最後期限時,連修復 Bug 的時間都不夠,我們確實很難為項目的每部分程式碼都編寫必要的文檔。但是,我希望通過這篇文章,讓你意識到文檔的重要性,并養成盡量編寫文檔的習慣。你不必在文檔中事無巨細地記錄所有細節,而應當對重要的信息進行強調,以便提醒其它開發者,或者讓自己在一段時間後能夠輕鬆地在原程式碼的基礎上繼續工作。除此之外,你還可以通過 Jazzy 將你的程式碼生成專業的幫助文檔。這會大大激發我們的工作慾望。愉快地在你程式碼中編寫文檔吧!

譯者簡介:楊宏焱,CSDN 博客專家(個人博客 http://blog.csdn.net/kmyhy)。2009 年開始學習蘋果 iOS 開發,精通 O-C/Swift 和 Cocoa Touch 框架,開發有多個商店應用和企業 App。熱愛寫作,著有多本技術專著,包括:《企業級 iOS 應用實戰》、《iPhone & iPad 企業移動應用開發秘笈》、《iOS8 Swift 編程指南》,《寫給大忙人看的 Swift》(合作翻譯)等。

原文Documenting Your Swift Code in Xcode Using Markdown

作者
Gabriel Theodoropoulos
資深軟體開發員,從事相關工作超過二十年,專門在不同的平台和各種程式語言去解決軟體開發問題。自2010年中,Gabriel專注在iOS程式的開發,利用教程與世界上每個角落的人分享知識。可以在Google+或推特關注 Gabriel。
評論
很好! 你已成功註冊。
歡迎回來! 你已成功登入。
你已成功訂閱 AppCoda 中文版 電子報。
你的連結已失效。
成功! 請檢查你的電子郵件以獲取用於登入的連結。
好! 你的付費資料已更新。
你的付費方式並未更新。